-
Notifications
You must be signed in to change notification settings - Fork 0
frontend unit+e2e tests #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds comprehensive frontend testing (Vitest + Playwright) and many test suites; centralizes frontend error handling with an error store and ErrorDisplay; introduces DATABASE_NAME for backend DB selection and updates code/tests; splits CI into backend/frontend workflows with a CI compose action; several frontend build, routing, proxy, and package changes. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (8)
frontend/vitest.setup.ts (1)
45-46: Simplify redundant ternary expression.The expression
query === '(prefers-color-scheme: dark)' ? false : falsealways returnsfalse. If you want to simulate system preference for dark mode, use a simple boolean or make the condition meaningful.🔎 Proposed fix
-vi.stubGlobal('matchMedia', vi.fn().mockImplementation((query: string) => ({ - matches: query === '(prefers-color-scheme: dark)' ? false : false, +vi.stubGlobal('matchMedia', vi.fn().mockImplementation((query: string) => ({ + matches: false,Or if you want to support dark mode testing:
-vi.stubGlobal('matchMedia', vi.fn().mockImplementation((query: string) => ({ - matches: query === '(prefers-color-scheme: dark)' ? false : false, +vi.stubGlobal('matchMedia', vi.fn().mockImplementation((query: string) => ({ + matches: query === '(prefers-color-scheme: dark)',frontend/playwright.config.ts (1)
16-21: Consider adding more browser projects for broader coverage.Currently only Chromium is configured. For production-grade E2E testing, consider adding Firefox and WebKit to catch browser-specific issues.
🔎 Suggested addition
projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, ],frontend/e2e/theme.spec.ts (1)
32-60: Tests bypass actual theme-switching mechanism.These tests manually manipulate
localStorageand DOM classes viapage.evaluate()instead of using the application's actual theme controls (buttons/selectors). This doesn't validate the real user flow.Consider clicking actual theme toggle buttons:
test('applies dark theme when set', async ({ page }) => { await page.goto('/login'); // Find and click the dark theme button/toggle await page.click('[data-testid="theme-toggle"]'); // or appropriate selector await page.click('[data-testid="theme-dark"]'); const hasDarkClass = await page.evaluate(() => document.documentElement.classList.contains('dark') ); expect(hasDarkClass).toBe(true); const storedTheme = await page.evaluate(() => localStorage.getItem('app-theme')); expect(storedTheme).toBe('dark'); });This approach validates the actual theme-switching logic and user interaction.
frontend/e2e/auth.spec.ts (2)
37-47: Document test credentials for environment setup.The tests use hardcoded credentials (
user/user123). Ensure these test users are documented in the project's testing setup guide, or consider using environment variables for flexibility across different testing environments.Example using environment variables:
const TEST_USERNAME = process.env.TEST_USERNAME || 'user'; const TEST_PASSWORD = process.env.TEST_PASSWORD || 'user123';
114-123: Fragile selector for logout button.The logout button selector uses a broad text-matching approach with multiple fallbacks. This is fragile and may break if button text changes or if multiple elements match.
🔎 Suggested improvement
Add a data-testid to the logout button in your component:
<button data-testid="logout-button" on:click={logout}>Logout</button>Then simplify the test:
- const logoutButton = page.locator('button:has-text("Logout"), a:has-text("Logout"), [data-testid="logout"]'); + const logoutButton = page.locator('[data-testid="logout-button"]');This makes tests more maintainable and resistant to UI text changes.
.github/workflows/frontend-tests.yml (2)
36-40: Consider removing the redundant test run.
npm run test:coveragealready runs all tests with coverage enabled. Runningnpm testfirst (line 37) and thennpm run test:coverage(line 40) duplicates test execution, increasing CI time without benefit.🔎 Proposed fix
- name: Install dependencies run: npm ci - - name: Run unit tests - run: npm test - - name: Run tests with coverage run: npm run test:coverage
59-62: Pin the yq version for reproducible builds.Downloading from
/latest/could introduce unexpected behavior if a breaking change is released. Pin to a specific version for CI stability.🔎 Proposed fix
- name: Install yq run: | - sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq + sudo wget https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64 -O /usr/local/bin/yq sudo chmod +x /usr/local/bin/yqfrontend/src/stores/__tests__/theme.test.ts (1)
150-157: Test assertion doesn't verify "without triggering server save".The test description states it "updates store without triggering server save", but there's no assertion verifying that
saveThemeSettingwas not called. Consider adding this verification for completeness.🔎 Proposed fix
describe('setThemeLocal', () => { it('updates store without triggering server save', async () => { + const { saveThemeSetting } = await import('../../lib/user-settings'); const { theme, setThemeLocal } = await import('../theme'); setThemeLocal('dark'); expect(get(theme)).toBe('dark'); expect(localStorage.setItem).toHaveBeenCalledWith('app-theme', 'dark'); + expect(saveThemeSetting).not.toHaveBeenCalled(); });
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (12)
.github/workflows/frontend-tests.yml(1 hunks)README.md(1 hunks)frontend/e2e/auth.spec.ts(1 hunks)frontend/e2e/theme.spec.ts(1 hunks)frontend/package.json(2 hunks)frontend/playwright.config.ts(1 hunks)frontend/src/stores/__tests__/auth.test.ts(1 hunks)frontend/src/stores/__tests__/notificationStore.test.ts(1 hunks)frontend/src/stores/__tests__/theme.test.ts(1 hunks)frontend/src/stores/__tests__/toastStore.test.ts(1 hunks)frontend/vitest.config.ts(1 hunks)frontend/vitest.setup.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/stores/__tests__/auth.test.ts (1)
frontend/src/stores/auth.ts (7)
username(44-44)userRole(46-46)csrfToken(48-48)verifyAuth(129-174)userId(45-45)userEmail(47-47)fetchUserProfile(95-103)
frontend/src/stores/__tests__/notificationStore.test.ts (1)
frontend/src/stores/notificationStore.ts (3)
notificationStore(99-99)notifications(101-101)unreadCount(100-100)
frontend/src/stores/__tests__/toastStore.test.ts (1)
frontend/src/stores/toastStore.ts (4)
toasts(13-13)addToast(16-25)removeToast(27-29)TOAST_DURATION(14-14)
frontend/src/stores/__tests__/theme.test.ts (1)
frontend/src/stores/theme.ts (4)
theme(39-51)toggleTheme(71-75)setTheme(77-79)setThemeLocal(81-87)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build Frontend
- GitHub Check: Backend Tests
🔇 Additional comments (17)
README.md (1)
19-23: LGTM! Clear separation of test status badges.The badge updates effectively distinguish between backend and frontend test workflows, improving visibility of CI status for each part of the codebase.
frontend/vitest.setup.ts (1)
1-75: Well-structured test setup with comprehensive mocks.The setup provides all necessary browser API mocks for isolated testing, including storage, media queries, and observers. The
resetStorageMockshelper ensures proper test isolation.frontend/vitest.config.ts (1)
1-34: LGTM! Well-configured Vitest setup for Svelte.The configuration appropriately:
- Disables hot reload during tests
- Uses jsdom for browser API simulation
- Includes coverage reporting with sensible exclusions
- Sets up proper path aliases
frontend/src/stores/__tests__/toastStore.test.ts (1)
1-162: Excellent test coverage for toast store.The test suite thoroughly validates:
- Message handling (objects, strings, primitives, null)
- Type defaults and explicit types
- Removal by ID with proper filtering
- Auto-removal timing with fake timers
- Multiple toast management
The use of fake timers ensures deterministic testing of time-based behavior.
frontend/playwright.config.ts (1)
1-29: Well-configured Playwright setup for local development.The configuration appropriately:
- Enables parallelism and CI-specific controls
- Handles self-signed HTTPS certificates
- Configures tracing and screenshots for debugging
- Reuses existing server in development
frontend/e2e/auth.spec.ts (1)
1-124: Comprehensive authentication flow coverage.The test suite thoroughly validates:
- Login UI presence and validation
- Error handling for invalid credentials
- Successful authentication flow
- Protected route redirects
- Redirect path preservation
- Registration navigation
- Logout functionality
frontend/src/stores/__tests__/auth.test.ts (1)
1-354: Excellent comprehensive test suite for auth store.The tests thoroughly validate:
- Initial state and localStorage restoration/expiration
- Login/logout flows with state management
- Token verification with caching and offline-first behavior
- Profile fetching and persistence
- Error handling across all operations
The use of
vi.resetModules()and dynamic imports ensures proper isolation between tests by resetting the store state. The mock setup is comprehensive and covers both success and failure scenarios..github/workflows/frontend-tests.yml (2)
64-70: LGTM on k3s setup.The k3s installation with
--disable=traefik --tls-san host.docker.internaland the subsequent kubeconfig extraction is correctly configured for CI.
183-196: Good failure diagnostics.Collecting and uploading logs on failure is a solid practice for debugging CI issues.
frontend/src/stores/__tests__/theme.test.ts (3)
1-16: LGTM on mock setup.Mocking
user-settingsandauthmodules before importing the theme module is the correct approach for isolating the store under test.
92-127: Good coverage of toggleTheme cycle.The tests correctly verify the cycle behavior matching the implementation:
light → dark → auto → light.
170-207: Auto theme tests are well structured.The matchMedia mock correctly simulates system preference detection for both light and dark modes.
frontend/src/stores/__tests__/notificationStore.test.ts (4)
4-15: LGTM on API mock setup.The mock functions are properly defined and the module mock correctly wraps them. This allows flexible test configuration per test case.
17-25: Good use of factory pattern for mock data.The
createMockNotificationhelper with sensible defaults and override support makes tests readable and maintainable.
197-213: Good edge case coverage for notification cap.Testing the 100-notification limit and verifying the newest notification replaces the oldest is valuable for ensuring the store doesn't grow unbounded.
388-441: LGTM on derived store tests.The
unreadCountderived store tests properly verify the count calculation and reactivity when notifications change state.frontend/package.json (1)
10-15: LGTM on test scripts.The test script organization follows common conventions:
testfor CI runs,test:watchfor development,test:uifor interactive debugging,test:coveragefor coverage reports, andtest:e2efor Playwright.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (12)
frontend/playwright.config.ts (1)
16-21: Consider expanding browser coverage.Currently only testing with Chromium. For more comprehensive cross-browser testing, consider adding Firefox and WebKit projects.
🔎 Example multi-browser configuration
projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, ],frontend/vitest.setup.ts (1)
45-46: Simplify redundant ternary expression.The ternary operator returns
falsefor both branches, making it unnecessarily complex.🔎 Proposed simplification
- matches: query === '(prefers-color-scheme: dark)' ? false : false, + matches: false,frontend/e2e/auth.spec.ts (2)
37-47: Verify test credentials match environment.The tests use hardcoded credentials
user/user123. Ensure these test credentials are properly configured in your test environment and documented for other developers.Consider extracting credentials to environment variables or a test configuration file:
const TEST_USERNAME = process.env.TEST_USERNAME || 'user'; const TEST_PASSWORD = process.env.TEST_PASSWORD || 'user123';
114-123: Consider using data-testid for more reliable selection.The logout button selector uses multiple fallback strategies which is flexible but could be brittle. Consider adding a
data-testidattribute to the logout button for more reliable testing.🔎 Recommended approach
In your component:
<button data-testid="logout">Logout</button>In the test:
const logoutButton = page.locator('[data-testid="logout"]');frontend/src/stores/__tests__/auth.test.ts (3)
36-42: Consider removing redundantvi.clearAllMocks()inafterEach.
vi.clearAllMocks()inafterEach(Line 42) is redundant sincemockXxx.mockReset()calls inbeforeEachalready clear the mocks before each test. Additionally,vi.resetModules()provides further isolation.🔎 Proposed simplification
- afterEach(() => { - vi.clearAllMocks(); - });
149-158: Consider asserting specific error content.The test uses
.rejects.toBeDefined()which only verifies an error was thrown, but doesn't validate the error message or type. This could mask unexpected error conditions.🔎 Proposed improvement
- await expect(login('baduser', 'badpass')).rejects.toBeDefined(); + await expect(login('baduser', 'badpass')).rejects.toThrow('Invalid credentials');
343-352: Same improvement opportunity for error assertion.Similar to the login test, consider asserting specific error content rather than just checking the error is defined.
🔎 Proposed improvement
- await expect(fetchUserProfile()).rejects.toBeDefined(); + await expect(fetchUserProfile()).rejects.toThrow('Unauthorized');frontend/e2e/theme.spec.ts (1)
62-74: Persistence test only verifies localStorage, not DOM state.This test confirms localStorage persists across navigation (which is expected browser behavior), but doesn't verify that the theme is actually applied after navigation. Consider also checking the DOM class state on
/register.🔎 Proposed enhancement
test('theme persists across page navigation', async ({ page }) => { await page.goto('/login'); // Set dark theme await page.evaluate(() => localStorage.setItem('app-theme', 'dark')); await page.reload(); // Navigate to another page await page.goto('/register'); const storedTheme = await page.evaluate(() => localStorage.getItem('app-theme')); expect(storedTheme).toBe('dark'); + + // Also verify the theme is applied on the new page + const hasDarkClass = await page.evaluate(() => + document.documentElement.classList.contains('dark') + ); + expect(hasDarkClass).toBe(true); });.github/workflows/frontend-tests.yml (4)
36-40: Running tests twice is redundant.Lines 37 and 40 run tests separately (
npm testthennpm run test:coverage). Coverage runs typically execute the same tests with instrumentation. Consider running only the coverage command.🔎 Proposed optimization
- - name: Run unit tests - run: npm test - - name: Run tests with coverage run: npm run test:coverage
59-62: Pin yq version for reproducible builds.Downloading from
/releases/latest/can cause unexpected behavior when a new version is released. Pin to a specific version for reproducibility.🔎 Proposed fix
- name: Install yq run: | - sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq + sudo wget https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64 -O /usr/local/bin/yq sudo chmod +x /usr/local/bin/yq
166-168: Consider installing all configured browsers or caching.Installing only
chromiumis fine if that's the only browser in the Playwright config. However, Playwright browser binaries are large; consider caching them to speed up CI.🔎 Example with caching
Add before the install step:
- name: Cache Playwright browsers uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }}
64-70: Pin k3s version for CI stability.The k3s install script can specify a version using the INSTALL_K3S_VERSION environment variable. Without pinning, the latest tag is not maintained, which can introduce unexpected variations between CI runs. For production workflows, specify a stable version:
- name: Setup Kubernetes (k3s) run: | - curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik --tls-san host.docker.internal" sh - + curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.33.4+k3s1" INSTALL_K3S_EXEC="--disable=traefik --tls-san host.docker.internal" sh -
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
.github/workflows/frontend-tests.yml(1 hunks).gitignore(1 hunks)README.md(1 hunks)frontend/e2e/auth.spec.ts(1 hunks)frontend/e2e/theme.spec.ts(1 hunks)frontend/package.json(3 hunks)frontend/playwright.config.ts(1 hunks)frontend/src/stores/__tests__/auth.test.ts(1 hunks)frontend/src/stores/__tests__/notificationStore.test.ts(1 hunks)frontend/src/stores/__tests__/theme.test.ts(1 hunks)frontend/src/stores/__tests__/toastStore.test.ts(1 hunks)frontend/vitest.config.ts(1 hunks)frontend/vitest.setup.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/stores/__tests__/notificationStore.test.ts (1)
frontend/src/stores/notificationStore.ts (3)
notificationStore(99-99)notifications(101-101)unreadCount(100-100)
frontend/src/stores/__tests__/toastStore.test.ts (1)
frontend/src/stores/toastStore.ts (4)
toasts(13-13)addToast(16-25)removeToast(27-29)TOAST_DURATION(14-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: E2E Tests
- GitHub Check: Build Frontend
- GitHub Check: Backend Tests
🔇 Additional comments (37)
README.md (1)
18-23: Badges correctly document the expanded testing infrastructure.The documentation update properly separates backend and frontend test badges, improving clarity. The new frontend-tests badge with Vitest branding aligns with the testing framework additions in this PR.
.gitignore (1)
94-97: LGTM!The additions appropriately ignore common test and coverage artifacts from the Vitest and Playwright testing frameworks being introduced in this PR. The entries follow the existing
.gitignorestructure and naming conventions.frontend/playwright.config.ts (2)
22-28: LGTM! Web server configuration is appropriate.The configuration correctly starts the dev server for tests with appropriate CI-aware server reuse and a generous timeout for slow environments.
11-12: No changes needed. TheignoreHTTPSErrors: truesetting is appropriate for this Playwright test configuration. Playwright provides ignoreHTTPSErrors to handle untrusted or invalid certificates particularly in development or testing environments. The configuration targetshttps://localhost:5001which is exclusively a local development server, and theplaywright.config.tsfile itself is a test-only configuration file never used in production environments.frontend/vitest.config.ts (3)
6-15: LGTM! Svelte plugin configuration is correct.The configuration properly disables hot reload during tests and enables Svelte 5 runes support, which aligns with modern Svelte development practices.
21-26: LGTM! Coverage configuration is well-structured.The coverage configuration appropriately includes TypeScript and Svelte files while excluding API clients and test files. The v8 provider with text and HTML reporters provides good coverage visibility.
28-34: LGTM! Module resolution is properly configured.The browser conditions and
$libalias are standard for Svelte projects and will ensure imports resolve correctly in tests.frontend/src/stores/__tests__/notificationStore.test.ts (6)
1-38: LGTM! Test setup is well-structured.The mock setup with
vi.resetModules()ensures proper test isolation, and thecreateMockNotificationfactory provides clean test data generation. The beforeEach/afterEach hooks properly clean up between tests.
40-55: LGTM! Initial state tests are comprehensive.The tests verify all aspects of the initial store state with proper use of dynamic imports to ensure fresh store instances after module resets.
57-177: LGTM! Load functionality tests are thorough.The test suite covers loading states, success/failure paths, return values, and API parameter passing. The async handling with proper await usage ensures reliable test execution.
179-214: LGTM! Add functionality tests cover key behaviors.The tests verify that notifications are added to the beginning and that the 100-item cap is enforced, which are the critical behaviors for this function.
216-370: LGTM! CRUD operation tests are comprehensive.The test suite thoroughly covers mark as read, mark all as read, delete, and clear operations with both success and failure paths, plus API call verification with correct parameters.
372-461: LGTM! Derived store tests verify reactivity.The tests verify that derived stores (unreadCount and notifications) properly react to changes in the base store, which is essential for store-based state management.
frontend/package.json (3)
10-15: LGTM! Test scripts are well-organized.The test scripts provide comprehensive testing options including watch mode, UI, coverage, and E2E tests. The naming conventions are clear and follow best practices.
26-26: No changes needed. The@mateothegreat/svelte5-routeris actively used throughout production code across multiple components and routes (App.svelte, Header.svelte, ProtectedRoute.svelte, Editor.svelte, Home.svelte, Login.svelte, Notifications.svelte, Register.svelte, and AdminLayout.svelte), confirming it belongs in runtime dependencies.
57-58: No migration action needed—Tailwind CSS v4 syntax is already correctly implemented.Tailwind CSS v4.0 introduces significant breaking changes, but the codebase has already been properly migrated. The app uses correct v4 syntax: @tailwindcss/postcss plugin in postcss.config.js, @import "tailwindcss" in CSS files instead of @tailwind directives, and CSS variables use parentheses instead of square brackets (e.g.,
var(--color-primary)). No old v3 patterns requiring migration were found in the frontend code.frontend/src/stores/__tests__/toastStore.test.ts (4)
5-14: LGTM! Fake timer setup is correct.The use of fake timers with proper cleanup ensures reliable testing of time-based auto-removal behavior without actual delays.
16-88: LGTM! AddToast tests cover all scenarios.The test suite comprehensively covers message handling including objects with message/detail properties, type coercion, null values, and ID uniqueness. This ensures robust toast creation.
90-119: LGTM! RemoveToast tests are thorough.The tests verify correct removal behavior including selective removal and graceful handling of non-existent IDs.
121-162: LGTM! Auto-removal tests verify timing behavior.The tests properly use fake timers to verify auto-removal timing including edge cases with multiple toasts at different offsets. The TOAST_DURATION constant test ensures the exported value is correct.
frontend/vitest.setup.ts (2)
4-22: LGTM! localStorage mock is complete.The mock implements all Storage interface methods with proper state management and function tracking, providing a reliable test double for browser storage.
56-75: LGTM! Observer mocks and reset helper are well-implemented.The ResizeObserver and IntersectionObserver mocks provide sufficient functionality for testing, and the
resetStorageMocks()helper ensures clean test isolation.frontend/e2e/auth.spec.ts (2)
3-24: LGTM! Login page tests verify basic functionality.The tests verify form element visibility and HTML5 validation attributes. The cookie clearing in beforeEach ensures a clean authentication state for each test.
63-101: LGTM! Protected route tests ensure proper authentication flow.The tests verify that unauthenticated users are redirected to login and that the intended destination is preserved, which is critical for good user experience. The registration link tests ensure proper navigation flow.
frontend/src/stores/__tests__/theme.test.ts (4)
4-38: LGTM! Test setup properly isolates theme store.The mock setup for user settings and auth stores, combined with module resets and localStorage mocking, ensures proper test isolation. The DOM cleanup prevents test interference.
40-90: LGTM! Initial state and set tests are comprehensive.The tests verify default behavior, localStorage loading, invalid value handling, and the complete flow of setting themes including persistence and DOM class manipulation.
92-168: LGTM! Toggle and setTheme tests verify cycling behavior.The tests thoroughly verify the theme toggle cycle and explicit theme setting, including both server-persisted and local-only changes. The full cycle test ensures the rotation works correctly.
170-227: LGTM! Auto theme tests verify system preference integration.The tests properly mock
matchMediato verify that auto theme correctly responds to system color scheme preferences, and the subscription test ensures reactive updates work correctly.frontend/src/stores/__tests__/auth.test.ts (5)
1-15: Well-structured mock setup.The API mocking approach using
vi.mockwith wrapper functions that delegate tovi.fn()instances is clean and allows for flexible mock control in each test.
20-42: Good test isolation withvi.resetModules().Using
vi.resetModules()inbeforeEachensures each test gets a fresh store instance, which is essential for testing Svelte stores that maintain module-level state. The localStorage mock implementation is also well done.
45-98: Thorough initial state tests including localStorage restoration and expiry.Good coverage of edge cases including expired auth state (25 hours). The timestamp-based expiry test at Line 89 effectively validates the 24-hour TTL logic.
212-229: Good resilience test for logout failure handling.This test verifies that local state is cleared even when the logout API call fails, which is the correct behavior for maintaining UI consistency.
301-322: Excellent offline-first verification test.This test properly validates that cached authentication state is returned when network errors occur, ensuring good UX during intermittent connectivity.
frontend/e2e/theme.spec.ts (1)
3-8: Good test setup with localStorage cleanup.The
beforeEachhook properly clears the theme preference to ensure test isolation..github/workflows/frontend-tests.yml (3)
1-14: Well-configured workflow triggers.The path filters ensure tests only run when relevant files change, which optimizes CI resources. Including
workflow_dispatchallows manual triggering for debugging.
183-196: Good failure diagnostics with log collection.Collecting Docker Compose logs on failure is excellent for debugging. The conditional
if: failure()ensures logs are captured when tests fail.
81-85: Dummy kubeconfig token should use actual k3s credentialsThe kubeconfig is intentionally created with
token: "ci-token"as a fallback in cert-generator/setup-k8s.sh when CI mode is enabled and kubectl connection fails. However, since k3s is running on the host and the real kubeconfig is accessible at ~/.kube/config (mounted in cert-generator), the setup script should attempt to extract the actual service account token or use the real kubeconfig from the k3s cluster instead of defaulting to a dummy token that cannot authenticate.If the backend services (k8s-worker, pod-monitor, result-processor) attempt k8s API operations during test execution, authentication will fail with the invalid token.
|
Codecov Report❌ Patch coverage is
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 69 files with indirect coverage changes 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
.github/workflows/frontend-tests.yml (1)
166-168: Frontend readiness check still masks failures with|| true.As noted in previous reviews, the
|| truesuffix allows the workflow to continue even if the frontend never becomes ready, potentially causing misleading E2E test failures.
🧹 Nitpick comments (4)
.github/workflows/frontend-tests.yml (4)
36-40: Consider removing the redundant test run.Running
npm testfollowed bynpm run test:coverageexecutes the test suite twice. Sincetest:coveragetypically runs all tests with coverage instrumentation, the separatenpm teststep appears unnecessary.🔎 Proposed optimization
- name: Install dependencies run: npm ci - - name: Run unit tests - run: npm test - - name: Run tests with coverage run: npm run test:coverage
74-77: Pin the yq version for reproducible builds.Downloading from
/latest/can introduce unexpected breaking changes if yq releases a new version with different behavior or CLI changes.🔎 Proposed fix
- name: Install yq run: | - sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq + sudo wget https://github.com/mikefarah/yq/releases/download/v4.44.6/yq_linux_amd64 -O /usr/local/bin/yq sudo chmod +x /usr/local/bin/yqCheck the yq releases page for the current stable version.
120-143: Consider a more maintainable approach for CI configuration.The 24 separate
yqcommands make this workflow fragile and difficult to maintain. If the basedocker-compose.yamlstructure changes, these commands may break or produce unexpected results.Consider one of these alternatives:
Maintain a separate
docker-compose.ci.yamlin the repository with CI-specific overrides using Docker Compose's native override mechanism:# docker-compose.ci.yaml in repo services: backend: environment: - TESTING=true - MONGO_ROOT_USER=root # ... other CI overridesThen use:
docker compose -f docker-compose.yaml -f docker-compose.ci.yaml upConsolidate yq modifications into a single multi-document operation or shell script in the repo for better version control and testing.
Use a yq script file (
.yq) committed to the repo that can be tested and reviewed separately.This would improve maintainability and make CI configuration changes reviewable via PR.
183-190: Consider collecting logs from additional services for comprehensive debugging.Currently, only frontend and backend logs are collected separately. Including logs from supporting services (kafka, zookeeper, mongo, redis) could help diagnose infrastructure-related E2E test failures.
🔎 Enhanced log collection
- name: Collect logs on failure if: failure() run: | mkdir -p logs docker compose -f docker-compose.ci.yaml logs > logs/docker-compose.log docker compose -f docker-compose.ci.yaml logs frontend > logs/frontend.log docker compose -f docker-compose.ci.yaml logs backend > logs/backend.log + docker compose -f docker-compose.ci.yaml logs kafka > logs/kafka.log + docker compose -f docker-compose.ci.yaml logs zookeeper > logs/zookeeper.log + docker compose -f docker-compose.ci.yaml logs mongo > logs/mongo.log
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/frontend-tests.yml(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Tests
- GitHub Check: Backend Tests
🔇 Additional comments (1)
.github/workflows/frontend-tests.yml (1)
87-108: Unable to verify review comment - repository access required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (3)
.github/workflows/ci.yml (2)
31-37: Redundant test execution.Running
npm test(line 33) followed bynpm run test:coverage(line 37) likely executes the test suite twice. Iftest:coverageruns tests with coverage, consider removing the plainnpm teststep or combining them.🔎 Proposed simplification
- name: Run frontend unit tests working-directory: frontend - run: npm test - - - name: Run frontend tests with coverage - working-directory: frontend - run: npm run test:coverage + run: npm run test:coverage
159-169: Hardcoded credentials in workflow.The MongoDB credentials (
root/rootpassword) are hardcoded. While acceptable for CI-only test databases, consider documenting this or using GitHub Actions variables for consistency. The Checkov hint (CKV_SECRET_4) flags this as a medium-severity finding.For better maintainability, define these as workflow-level environment variables or use repository variables:
env: MONGO_TEST_USER: root MONGO_TEST_PASSWORD: rootpassword.github/actions/setup-ci-compose/action.yml (1)
26-29: Credentials duplicated between action and workflow.The credentials
MONGO_ROOT_USER=rootandMONGO_ROOT_PASSWORD=rootpasswordare hardcoded here and also in the main workflow (lines 162-165). Consider parameterizing these as action inputs to maintain a single source of truth.🔎 Example parameterization
Add inputs to the action:
inputs: kubeconfig-path: description: Path to kubeconfig file for cert-generator mount required: true mongo-user: description: MongoDB root user default: 'root' mongo-password: description: MongoDB root password default: 'rootpassword'Then use
${{ inputs.mongo-user }}and${{ inputs.mongo-password }}in the yq commands.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
.github/actions/setup-ci-compose/action.yml(1 hunks).github/workflows/ci.yml(1 hunks).github/workflows/tests.yml(0 hunks)README.md(1 hunks)deploy.sh(1 hunks)frontend/e2e/theme.spec.ts(1 hunks)frontend/playwright.config.ts(1 hunks)
💤 Files with no reviewable changes (1)
- .github/workflows/tests.yml
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/playwright.config.ts
- frontend/e2e/theme.spec.ts
- README.md
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/ci.yml
53-53: shellcheck reported issue in this script: SC2024:warning:3:36: sudo doesn't affect redirects. Use ..| sudo tee file
(shellcheck)
53-53: shellcheck reported issue in this script: SC2086:info:2:10: Double quote to prevent globbing and word splitting
(shellcheck)
53-53: shellcheck reported issue in this script: SC2086:info:3:38: Double quote to prevent globbing and word splitting
(shellcheck)
53-53: shellcheck reported issue in this script: SC2086:info:4:16: Double quote to prevent globbing and word splitting
(shellcheck)
176-176: input "file" is not defined in action "codecov/codecov-action@v5". available inputs are "base_sha", "binary", "codecov_yml_path", "commit_parent", "directory", "disable_file_fixes", "disable_safe_directory", "disable_search", "disable_telem", "dry_run", "env_vars", "exclude", "fail_ci_if_error", "files", "flags", "force", "gcov_args", "gcov_executable", "gcov_ignore", "gcov_include", "git_service", "handle_no_reports_found", "job_code", "name", "network_filter", "network_prefix", "os", "override_branch", "override_build", "override_build_url", "override_commit", "override_pr", "plugins", "recurse_submodules", "report_code", "report_type", "root_dir", "run_command", "skip_validation", "slug", "swift_project", "token", "url", "use_legacy_upload_endpoint", "use_oidc", "use_pypi", "verbose", "version", "working-directory"
(action)
🪛 Checkov (3.2.334)
.github/workflows/ci.yml
[medium] 165-166: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Tests
🔇 Additional comments (6)
deploy.sh (1)
104-104: The frontend service is properly configured to serve HTTPS on port 5001. The rollup.config.js file includes a custom HTTPS server that starts withnpm run dev, using certificates generated by the cert-generator service and mounted at./frontend/certs:/app/certs. The Playwright baseURL setting tohttps://localhost:5001is correct and aligns with the actual configuration.Likely an incorrect or invalid review comment.
.github/workflows/ci.yml (4)
185-196: E2E tests run even after backend test failures.The condition
if: success() || failure()means E2E tests run regardless of backend test results (but not on cancellation). This may be intentional for comprehensive reporting, but could waste CI time on known-broken builds.If this is intentional to always gather E2E results, consider adding a comment explaining the rationale. Otherwise, remove the condition to fail fast:
- name: Install Playwright browsers - if: success() || failure() working-directory: frontend run: npx playwright install chromium
198-203: Verify artifact action version.Same concern as earlier—
actions/upload-artifact@v6may not exist. Consider using v4.
40-40:actions/upload-artifact@v6is available and is the current latest version.actions/upload-artifact@v6 now runs on Node.js 24 (runs.using: node24) and requires a minimum Actions Runner version of 2.327.1. No action needed.
15-15: Theactions/checkout@v6is available and was released on November 20, 2025. The latest version is v6.0.0, published November 20, 2025. The workflow will not fail due to this action version not existing..github/actions/setup-ci-compose/action.yml (1)
1-55: Overall: Well-structured composite action.The action cleanly encapsulates CI-specific Docker Compose modifications. The use of yq for declarative YAML transformations is appropriate and maintainable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
.github/workflows/ci.yml (3)
133-136: Consider removing silent failure for frontend health check.Past review comment flagged this: The
|| truecauses this step to always succeed even if the frontend never starts. This could lead to confusing E2E test failures.Consider failing fast or adding explicit logging:
🔎 Alternative approaches
Option 1: Remove
|| trueto fail fast:- curl --retry 30 --retry-delay 5 --retry-all-errors -ksf https://127.0.0.1:5001/ || true + curl --retry 30 --retry-delay 5 --retry-all-errors -ksf https://127.0.0.1:5001/Option 2: Add explicit warning:
- curl --retry 30 --retry-delay 5 --retry-all-errors -ksf https://127.0.0.1:5001/ || true + curl --retry 30 --retry-delay 5 --retry-all-errors -ksf https://127.0.0.1:5001/ || echo "::warning::Frontend health check failed"Based on past review comments.
172-182: Fix Codecov action input parameter name.Past review comment and static analysis both confirm: The codecov action v5 uses
files(plural), notfile. This will cause the coverage upload to fail.🔎 Proposed fix
- name: Upload backend coverage to Codecov uses: codecov/codecov-action@v5 if: always() with: token: ${{ secrets.CODECOV_TOKEN }} - file: backend/coverage.xml + files: backend/coverage.xml flags: backend name: backend-coverage slug: HardMax71/Integr8sCode fail_ci_if_error: falseBased on past review comments and static analysis (actionlint).
56-56: Fix sudo redirect issue.Past review comment flagged this:
sudodoesn't affect the redirect operator (>). The file will be written with the runner's permissions, not elevated privileges. This could cause permission issues.🔎 Proposed fix using sudo tee
- sudo k3s kubectl config view --raw > /home/runner/.kube/config + sudo k3s kubectl config view --raw | sudo tee /home/runner/.kube/config > /dev/nullBased on past review comments and static analysis (actionlint).
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/ci.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/ci.yml
177-177: input "file" is not defined in action "codecov/codecov-action@v5". available inputs are "base_sha", "binary", "codecov_yml_path", "commit_parent", "directory", "disable_file_fixes", "disable_safe_directory", "disable_search", "disable_telem", "dry_run", "env_vars", "exclude", "fail_ci_if_error", "files", "flags", "force", "gcov_args", "gcov_executable", "gcov_ignore", "gcov_include", "git_service", "handle_no_reports_found", "job_code", "name", "network_filter", "network_prefix", "os", "override_branch", "override_build", "override_build_url", "override_commit", "override_pr", "plugins", "recurse_submodules", "report_code", "report_type", "root_dir", "run_command", "skip_validation", "slug", "swift_project", "token", "url", "use_legacy_upload_endpoint", "use_oidc", "use_pypi", "verbose", "version", "working-directory"
(action)
🪛 Checkov (3.2.334)
.github/workflows/ci.yml
[medium] 166-167: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Tests
🔇 Additional comments (4)
.github/workflows/ci.yml (4)
17-45: Frontend test setup looks solid.The Node.js setup with npm caching, test execution, and coverage artifact upload follows CI best practices.
90-107: Efficient parallel image pulling.Running pulls in parallel with
&andwaitis an excellent optimization for CI build times.
186-205: Well-structured E2E test execution.The use of
if: success() || failure()ensures E2E tests run even if backend tests fail, maximizing diagnostic information from each CI run.
209-227: Comprehensive log collection for debugging.Collecting both Docker Compose and Kubernetes logs with
if: always()ensures diagnostic information is available for troubleshooting failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
78-86: Shell scripting issues remain unaddressed.Two issues persist from previous reviews:
Line 82:
sudodoesn't affect redirects—the config file won't be written with proper permissions. Usesudo teeinstead.Line 84:
export KUBECONFIGin a run step doesn't persist to subsequent steps in GitHub Actions.🔎 Recommended fix
- name: Setup Kubernetes (k3s) run: | curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik --tls-san host.docker.internal" sh - mkdir -p /home/runner/.kube - sudo k3s kubectl config view --raw > /home/runner/.kube/config + sudo k3s kubectl config view --raw | sudo tee /home/runner/.kube/config > /dev/null sudo chmod 600 /home/runner/.kube/config - export KUBECONFIG=/home/runner/.kube/config timeout 90 bash -c 'until sudo k3s kubectl cluster-info; do sleep 5; done'Then add
KUBECONFIGto the job-level environment:frontend-e2e: name: Frontend E2E Tests needs: frontend-unit runs-on: ubuntu-latest + env: + KUBECONFIG: /home/runner/.kube/config steps:Based on past review comments.
190-197: Same shell scripting issues as frontend-e2e job.Lines 194 and 196 have the same issues flagged in the frontend-e2e job:
sudoredirect doesn't work as expectedexport KUBECONFIGdoesn't persist to subsequent stepsApply the same fixes as recommended for lines 78-86, and add job-level
KUBECONFIGenvironment variable.Based on past review comments.
🧹 Nitpick comments (1)
frontend/e2e/auth.spec.ts (1)
55-66: Optional: Strengthen loading state verification.The regex
/Logging in|Sign in/accepts both the loading and non-loading states, so the test passes whether or not the loading state actually appears. This reduces the test's ability to catch regressions.Consider checking for the loading state more specifically, or verifying the button is disabled during submission.
🔎 Alternative approach
test('shows loading state during login', async ({ page }) => { await page.goto('/login'); await page.waitForSelector('#username'); await page.fill('#username', 'user'); await page.fill('#password', 'user123'); const submitButton = page.locator('button[type="submit"]'); + + // Verify button is enabled before click + await expect(submitButton).toBeEnabled(); + await submitButton.click(); - await expect(submitButton).toContainText(/Logging in|Sign in/); + // Verify loading state appears (button disabled or shows loading text) + await expect(submitButton).toBeDisabled(); + // Or more specifically check for loading text: + // await expect(submitButton).toContainText('Logging in'); });
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/ci.yml(1 hunks)frontend/e2e/auth.spec.ts(1 hunks)
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/ci.yml
[medium] 259-260: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (16)
frontend/e2e/auth.spec.ts (8)
3-12: LGTM - thorough auth state cleanup.The beforeEach hook properly clears all authentication state (cookies, localStorage, sessionStorage) to ensure test isolation.
14-23: LGTM - comprehensive login page verification.The test properly waits for the form to render and verifies all essential login elements are visible.
33-42: LGTM - proper error handling verification.The test correctly verifies that invalid credentials display an error message.
44-53: LGTM - successful login flow verified.The test correctly verifies that valid credentials redirect to the editor page.
68-73: LGTM - auth guard protection verified.The test correctly verifies that unauthenticated users are redirected from protected routes.
75-87: LGTM - excellent redirect preservation test.This test verifies an important UX feature: preserving the intended destination after authentication.
89-96: LGTM - registration link verified.
98-105: LGTM - registration navigation verified..github/workflows/ci.yml (8)
14-45: LGTM - clean frontend unit test job.The job properly sets up Node.js, installs dependencies, runs tests with coverage, and uploads artifacts.
87-107: LGTM - kubeconfig creation for CI.The heredoc properly generates a test kubeconfig for the CI environment.
109-127: LGTM - efficient build with layer caching.The docker/bake-action setup with GitHub Actions cache provides good build performance with proper cache scope isolation per branch.
128-139: LGTM - proper health check implementation.The service readiness checks will now fail appropriately if services don't start, ensuring test reliability.
141-168: LGTM - comprehensive test execution and failure diagnostics.The job properly runs E2E tests, uploads reports, and collects detailed logs on failure for troubleshooting.
221-249: LGTM - consistent infrastructure setup.The backend integration job follows the same solid patterns as the frontend E2E job with appropriate service dependencies.
251-274: LGTM - proper test execution with coverage.The integration tests are well-configured with:
- Appropriate timeout
- Test-specific environment variables and schema prefix for isolation
- Coverage reporting to Codecov with correct parameters
The hardcoded MongoDB credentials flagged by static analysis are acceptable test credentials for the CI environment.
276-291: LGTM - comprehensive failure diagnostics.Excellent log collection covering Docker services and Kubernetes resources. The
|| trueon kubectl commands is appropriate here to ensure log collection doesn't fail if cluster resources are unavailable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
194-201: Same KUBECONFIG export issue as frontend-e2e job.The
export KUBECONFIGon line 200 has the same limitation as in the frontend-e2e job (line 84). It only persists for the timeout command on line 201.See the earlier comment on lines 78-86 for the recommended fix.
78-86: KUBECONFIG export persists only within the current step.<!-- [scratchpad_start]
[task review_file_3/3 review_k3s_setup_1]
Review k3s setup in frontend-e2e job
[observations]
- Line 84: export KUBECONFIG=/home/runner/.kube/config
- Web search confirms: export in GitHub Actions run step does NOT persist to subsequent steps
- Each step gets its own shell process
- To persist across steps, need to use $GITHUB_ENV or job-level env
[analysis]
The export on line 84 only affects the timeout command on line 85 within the same step
Subsequent steps that might need kubectl won't have KUBECONFIG set
However, looking at the workflow:- Line 112: kubeconfig-path is passed to setup-ci-compose action
- No other steps seem to directly use kubectl in frontend-e2e job
- The export is only used for the timeout check on line 85
Severity: This is technically correct for the timeout command but could cause issues if kubectl is used elsewhere
Actually, looking more carefully, the export is used in the timeout command on line 85 which is in the same step, so it works for that purpose
But the step name "Setup Kubernetes (k3s)" suggests this is setting up k8s for later use, which would require KUBECONFIG to persist
[actions]
Flag this as a potential issue with request_verification to check if kubectl is used in subsequent steps
[scratchpad_end] -->The
export KUBECONFIGat line 84 only persists within that step (for the timeout command on line 85). Each run step gets its own shell instance, so exports don't carry over between steps.However, examining the workflow, I see that:
- The export is used immediately on line 85 (same step) ✅
- Line 112 passes the kubeconfig path explicitly to the action ✅
- No other steps in this job directly invoke
kubectlSo the current implementation appears functional, but it's fragile. If future steps need kubectl access, they won't have KUBECONFIG set.
🔎 Recommended fix for robustness
To persist environment variables across steps, write to the $GITHUB_ENV file.
- name: Setup Kubernetes (k3s) run: | curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik --tls-san host.docker.internal" sh - mkdir -p /home/runner/.kube sudo k3s kubectl config view --raw > /home/runner/.kube/config sudo chmod 600 /home/runner/.kube/config - export KUBECONFIG=/home/runner/.kube/config + echo "KUBECONFIG=/home/runner/.kube/config" >> $GITHUB_ENV timeout 90 bash -c 'until sudo k3s kubectl cluster-info; do sleep 5; done'Or, set it at job-level if it's constant:
frontend-e2e: name: Frontend E2E Tests needs: frontend-unit runs-on: ubuntu-latest env: KUBECONFIG: /home/runner/.kube/config steps: # ...
🧹 Nitpick comments (3)
frontend/rollup.config.js (1)
65-72: Consider increasing the socket timeout for development.The 2-second timeout is quite aggressive and may cause issues during debugging or on slower networks. Consider increasing it to 5-10 seconds for a better development experience while still preventing indefinite hangs.
🔎 Suggested adjustment
proxyReq.on('socket', (socket) => { - socket.setTimeout(2000); + socket.setTimeout(5000); // 5s is more forgiving during development socket.on('timeout', () => { console.error('Proxy socket timeout - backend unreachable'); proxyReq.destroy(new Error('Socket timeout')); }); });frontend/playwright.config.ts (1)
1-34: LGTM! Well-structured Playwright configuration.The configuration is properly set up with:
- Appropriate timeouts for E2E tests (10s per test, 3s for assertions)
- CI-aware settings (retries, workers, reporters)
- Conditional webServer that starts dev server locally but relies on docker-compose in CI
- HTTPS error handling for self-signed certificates
💡 Optional: Consider adding Firefox for broader coverage
While Chromium coverage is sufficient for most cases, you could optionally add Firefox to catch browser-specific issues:
projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, ],This can be deferred if it increases CI time significantly.
.github/workflows/ci.yml (1)
78-85: Consider extracting duplicate k3s setup into a reusable action.The k3s setup code is duplicated between the
frontend-e2e(lines 78-85) andbackend-integration(lines 194-201) jobs. Similarly, the kubeconfig creation (lines 87-107 and 203-223) is duplicated.💡 Refactoring suggestion
Create a composite action
.github/actions/setup-k3s/action.yml:name: 'Setup k3s' description: 'Install k3s and create kubeconfig' outputs: kubeconfig-path: description: 'Path to kubeconfig' value: /home/runner/.kube/config runs: using: 'composite' steps: - name: Setup Kubernetes (k3s) shell: bash run: | curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik --tls-san host.docker.internal" sh - mkdir -p /home/runner/.kube sudo k3s kubectl config view --raw | sudo tee /home/runner/.kube/config > /dev/null sudo chmod 600 /home/runner/.kube/config echo "KUBECONFIG=/home/runner/.kube/config" >> $GITHUB_ENV timeout 90 bash -c 'until sudo k3s kubectl cluster-info; do sleep 5; done' - name: Create kubeconfig for CI shell: bash run: | cat > backend/kubeconfig.yaml <<EOF apiVersion: v1 kind: Config clusters: - name: ci-cluster cluster: server: https://host.docker.internal:6443 insecure-skip-tls-verify: true users: - name: ci-user user: token: "ci-token" contexts: - name: ci context: cluster: ci-cluster user: ci-user current-context: ci EOFThen in both jobs:
- name: Setup k3s uses: ./.github/actions/setup-k3sThis would eliminate ~40 lines of duplicated code and improve maintainability.
Also applies to: 194-201
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/ci.ymlfrontend/playwright.config.tsfrontend/rollup.config.js
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/ci.yml
[medium] 263-264: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (4)
frontend/rollup.config.js (1)
74-80: LGTM! Excellent error handling improvement.The
headersSentcheck prevents "Cannot set headers after they are sent" errors, which is a common pitfall in Node.js proxy implementations. The socket timeout addition also prevents hanging requests when the backend is unreachable..github/workflows/ci.yml (3)
14-45: LGTM! Clean frontend unit test job.The job is well-structured with proper Node.js setup, caching, and artifact uploads. Running both regular tests and coverage tests ensures comprehensive reporting.
133-143: LGTM! Robust health checks with proper failure handling.The critical health checks (lines 136, 138) correctly fail the step if services don't start. The supplementary checks (lines 140, 142) provide useful debugging context without masking failures. This is a good balance between reliability and debuggability.
269-279: LGTM! Correct Codecov configuration.The Codecov action is properly configured with
files:(plural) input, which is the correct parameter name for v5 of the action. The configuration includes appropriate flags, error handling, and slug specification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
264-276: Review hardcoded credentials flagged by past comments and static analysis.As noted in previous reviews, the hardcoded MongoDB credentials on lines 268-269 should be moved to GitHub Secrets per security best practices, even though they are ephemeral CI credentials. Additionally, the static analysis tool (Checkov) is flagging lines 272-273 for basic auth credentials in the MongoDB URL.
While these are test credentials scoped to CI, consider using GitHub Secrets for consistency with security best practices. See the previous review comment for the recommended approach using
${{ secrets.MONGO_TEST_USER }}and${{ secrets.MONGO_TEST_PASSWORD }}.
78-86: Address shell quoting and KUBECONFIG persistence issues flagged in past reviews.Two issues remain from previous review comments:
- Shell quoting: Variables like
$HOME(or the hardcoded path) should be properly quoted, and thesudoredirect issue should be fixed withsudo tee.- KUBECONFIG export: The
export KUBECONFIGon line 84 won't persist to subsequent steps. This needs to be set at the job level or in each step that requires it.Based on past review comments, these issues were identified but may not have been fully addressed. Please review the previous suggestions for the complete fix.
🧹 Nitpick comments (1)
frontend/playwright.config.ts (1)
14-19: Consider optimizing trace capture to reduce overhead.The
trace: 'on'setting captures traces for all tests, including successful ones, which can consume significant storage and slow down test execution. Consider using'retain-on-failure'or'on-first-retry'to capture traces only when needed for debugging.🔎 Proposed optimization
use: { baseURL: 'https://localhost:5001', ignoreHTTPSErrors: true, - trace: 'on', + trace: 'retain-on-failure', screenshot: 'only-on-failure', },Alternative: Use
'on-first-retry'if you want traces only when tests are retried.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/ci.ymlfrontend/e2e/auth.spec.tsfrontend/playwright.config.tsfrontend/src/main.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/e2e/auth.spec.ts
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/main.ts (1)
frontend/src/lib/api/client.gen.ts (1)
client(16-16)
🪛 Checkov (3.2.334)
.github/workflows/ci.yml
[medium] 272-273: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (6)
frontend/playwright.config.ts (1)
27-33: LGTM! Well-configured webServer for local development.The webServer configuration appropriately starts the dev server only in local environments (not CI), with proper timeout and SSL handling. The
reuseExistingServer: truesetting is particularly useful for developer experience..github/workflows/ci.yml (4)
17-44: LGTM! Well-structured frontend unit test job.The job is properly configured with:
- Node.js 22 and npm caching for fast installs
- Separate test and coverage steps for clarity
- Coverage artifact upload with
if: always()to ensure coverage is captured even on test failure
133-143: Excellent health check and connectivity validation.The service readiness checks are thorough and well-designed:
- Appropriate retry counts and delays (60 retries × 5s = 5 min timeout)
- Tests both frontend and backend independently
- Validates frontend→backend proxy connectivity
- Tests actual auth endpoint with clear warning messages
- Uses
|| echo "WARNING:..."pattern to provide debugging info without failing the stepThis robust validation will catch configuration issues early.
145-152: Helpful debugging step for E2E test failures.The debug frontend state step provides valuable diagnostic information by testing the auth API directly and checking for rendered form elements. The 5-second sleep gives JavaScript time to load, and the grep pattern covers multiple expected elements.
289-304: Comprehensive failure log collection.The log collection on failure is well-implemented:
- Captures Docker Compose logs for all services
- Includes Kubernetes events and pod descriptions
- Uses
|| trueappropriately to prevent log collection failures from masking the actual test failure- Organized into separate files for easier debugging
frontend/src/main.ts (1)
6-11: Configuration is correct and verified.The baseUrl setting has been confirmed. The SDK generated by @hey-api/openapi-ts uses full absolute paths for all API endpoints (e.g.,
/api/v1/auth/login,/api/v1/execute,/api/v1/events/notifications/stream), so settingbaseUrl: ''is appropriate and all API calls will work correctly with this configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
frontend/src/stores/__tests__/notificationStore.test.ts (1)
1-465: Exemplary test suite with comprehensive coverage.This test file demonstrates excellent testing practices:
- Well-structured with clear describe blocks grouping related functionality
- Comprehensive coverage of all store methods (load, add, markAsRead, markAllAsRead, delete, clear, refresh)
- Both success and failure paths tested consistently
- State mutations, return values, and API invocations all verified
- Derived stores tested for correctness and reactivity
- Proper test isolation via module resets and dynamic imports
- Good edge case coverage (100-item cap, various parameter combinations)
Optional enhancement: Verify mock API signatures match actual implementation.
While the mocks appear correctly structured, it would be beneficial to verify they match the actual API signatures, especially since this is the first time comprehensive frontend tests are being added.
🔎 Verification script
#!/bin/bash # Verify that mocked API functions exist in the actual API module echo "=== Checking API functions referenced in mocks ===" echo "" # Search for the API function definitions/exports echo "1. Checking getNotificationsApiV1NotificationsGet:" ast-grep --pattern 'export $_ getNotificationsApiV1NotificationsGet' echo "" echo "2. Checking markNotificationReadApiV1NotificationsNotificationIdReadPut:" ast-grep --pattern 'export $_ markNotificationReadApiV1NotificationsNotificationIdReadPut' echo "" echo "3. Checking markAllReadApiV1NotificationsMarkAllReadPost:" ast-grep --pattern 'export $_ markAllReadApiV1NotificationsMarkAllReadPost' echo "" echo "4. Checking deleteNotificationApiV1NotificationsNotificationIdDelete:" ast-grep --pattern 'export $_ deleteNotificationApiV1NotificationsNotificationIdDelete' echo "" echo "=== Checking actual API module exports ===" rg -n "export.*(?:getNotifications|markNotificationRead|markAllRead|deleteNotification)" --type ts frontend/src/lib/frontend/src/App.svelte (1)
30-32: Type mismatch with store value.The
globalErrortype is{ error: Error | string; title?: string }butappErrorstore emitsAppError | nullwhich also includes atimestampfield. While this works because extra properties are ignored, consider using theAppErrortype directly for consistency.- let globalError = $state<{ error: Error | string; title?: string } | null>(null); + import type { AppError } from './stores/errorStore'; + let globalError = $state<AppError | null>(null);frontend/src/components/ErrorDisplay.svelte (1)
11-17: Consider using SPA navigation for "Go to Home" action.Using
window.location.href = '/'causes a full page reload. If the error is recoverable and doesn't require a full reload, consider using the SPA router's navigation. However, given this is for fatal errors, a full reload may be intentional to reset application state.frontend/src/stores/errorStore.ts (1)
9-10: Unusedupdateimport.The
updatefunction is destructured fromwritable()but never used. Consider removing it to keep the code clean.- const { subscribe, set, update } = writable<AppError | null>(null); + const { subscribe, set } = writable<AppError | null>(null);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/src/App.sveltefrontend/src/components/ErrorDisplay.sveltefrontend/src/main.tsfrontend/src/stores/__tests__/notificationStore.test.tsfrontend/src/stores/errorStore.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/stores/__tests__/notificationStore.test.ts (1)
frontend/src/stores/notificationStore.ts (3)
notificationStore(99-99)notifications(101-101)unreadCount(100-100)
frontend/src/main.ts (2)
frontend/src/lib/api/client.gen.ts (1)
client(16-16)frontend/src/stores/errorStore.ts (1)
appError(26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Scan Frontend
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (17)
frontend/src/stores/__tests__/notificationStore.test.ts (11)
1-29: Excellent mock setup and test utilities.The mock infrastructure is well-designed:
- API functions are properly mocked with
vi.fn()and intercepted viavi.mock()- The
createMockNotificationfactory provides realistic defaults with random IDs to prevent collisions- Override pattern with spread operator is clean and flexible
31-42: Proper test isolation with module resets.The
vi.resetModules()inbeforeEachcombined with dynamic imports throughout the tests ensures each test gets a fresh store instance, preventing state leakage between tests. This is the correct pattern for testing Svelte stores.
44-59: LGTM! Clean initial state verification.The tests properly verify the store's initial state with clear, focused assertions.
61-181: Comprehensive coverage of the load() method.The tests thoroughly verify:
- Loading state transitions
- Success and failure paths
- Return values in both scenarios
- API parameter forwarding (limit and tag filters)
The 10ms timeout on Line 66 is acceptable for unit tests, though it could theoretically be flaky in extremely constrained environments.
183-218: Well-tested add behavior including boundary conditions.The tests correctly verify:
- New notifications are prepended to the array
- The 100-item cap is enforced with oldest items dropped
Good coverage of the edge case behavior.
220-267: Thorough markAsRead test coverage.The tests verify success/failure paths, return values, state mutations, and correct API invocations. Well-structured.
269-300: Clean markAllAsRead verification.Both success and failure paths are properly tested with appropriate assertions.
302-352: Complete delete behavior verification.The tests cover success/failure paths, state changes, and API invocations thoroughly.
354-374: LGTM! Straightforward clear() verification.
376-390: Refresh delegation verified correctly.The test confirms that
refresh()callsload()with the expected default limit (20) and undefined tag filters.
392-464: Excellent derived store testing.The tests verify:
unreadCountcorrectly filters and counts non-read notifications- Both derived stores react properly to state changes
- Edge cases (all read, updates after marking)
Well-structured reactive behavior verification.
frontend/src/App.svelte (1)
196-204: LGTM! Error display takes precedence correctly.The rendering order appropriately prioritizes displaying global errors before the loading spinner or main router content. This ensures users see error states immediately rather than being stuck on a loading screen.
frontend/src/components/ErrorDisplay.svelte (1)
20-71: Well-structured error display component.The component provides a clean, accessible error UI with appropriate dark mode support, clear action buttons, and optional technical details. The design follows established patterns in the codebase.
frontend/src/stores/errorStore.ts (1)
1-26: Clean error store implementation.The store follows Svelte conventions with proper TypeScript typing. The timestamped errors and console logging provide good debugging support.
frontend/src/main.ts (3)
16-19: Returningtruesuppresses console errors in DevTools.By returning
truefromwindow.onerror, the default browser error logging is suppressed. While the customconsole.errorcall logs the error, developers may expect to see the standard red error formatting in DevTools. Consider whether this suppression is intentional.
27-43: Good fallback for mount failures.The try-catch around
mount()with anErrorDisplayfallback ensures users see a meaningful error UI even if the main application fails to initialize. This is a solid defensive pattern.One consideration: if
ErrorDisplayitself fails to mount, the error would be unhandled. This is an edge case but worth noting.
8-13: Empty baseUrl with absolute API paths is correct.The SDK generates endpoints with full absolute paths (e.g.,
/api/v1/auth/login), making an emptybaseUrlthe appropriate configuration. This won't break on subpath deployments since absolute paths resolve relative to the current origin, independent of frontend routing configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
.github/actions/setup-ci-compose/action.yml (3)
12-16: Add checksum verification for the yq binary.Downloading binaries without checksum verification poses a security risk. If the download is compromised, a malicious binary could be executed in the CI environment.
🔎 Proposed fix with SHA-256 verification
- name: Install yq shell: bash run: | - sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.50.1/yq_linux_amd64 + sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.50.1/yq_linux_amd64 + echo "c35f640bf4fa5c4e8c2d8f97d3c057aa8f52c7cfce24f3ccb0c07c6fc3aa8cba /usr/local/bin/yq" | sha256sum -c - sudo chmod +x /usr/local/bin/yq
22-23: Add validation for docker-compose.yaml existence.If
docker-compose.yamldoes not exist, thecpcommand will fail, and subsequentyqcommands will produce unclear error messages.🔎 Proposed fix
run: | + if [ ! -f docker-compose.yaml ]; then + echo "Error: docker-compose.yaml not found" + exit 1 + fi cp docker-compose.yaml docker-compose.ci.yaml
26-29: Consider consolidating multiple yq calls for efficiency.Four separate
yq evalcalls are used to add backend environment variables. While functionally correct, this can be consolidated into a single call for better efficiency.🔎 Proposed consolidation
- # Backend environment variables - yq eval '.services.backend.environment += ["TESTING=true"]' -i docker-compose.ci.yaml - yq eval '.services.backend.environment += ["MONGO_ROOT_USER=root"]' -i docker-compose.ci.yaml - yq eval '.services.backend.environment += ["MONGO_ROOT_PASSWORD=rootpassword"]' -i docker-compose.ci.yaml - yq eval '.services.backend.environment += ["OTEL_SDK_DISABLED=true"]' -i docker-compose.ci.yaml + # Backend environment variables + yq eval '.services.backend.environment += ["TESTING=true", "MONGO_ROOT_USER=root", "MONGO_ROOT_PASSWORD=rootpassword", "OTEL_SDK_DISABLED=true"]' -i docker-compose.ci.yaml
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.github/actions/setup-ci-compose/action.ymlfrontend/e2e/auth.spec.tsfrontend/rollup.config.jsfrontend/src/App.sveltefrontend/src/routes/Login.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/e2e/auth.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (13)
.github/actions/setup-ci-compose/action.yml (4)
31-36: Volume removal logic is correct.The use of
selectfilters to remove hot-reload volume mounts is appropriate for CI environments where these mounts can cause permission issues and slow rebuilds.
38-43: Kafka/Zookeeper simplifications are appropriate for CI.Disabling SASL authentication and removing related configuration volumes is a reasonable simplification for the CI test environment.
45-48: Zookeeper simplification is appropriate for CI.Removing the healthcheck and using
service_startedcondition simplifies the CI environment without compromising test functionality.
50-53: Cert-generator configuration looks good.The use of
uniquefilters on lines 51-52 correctly prevents duplicate entries on re-runs. The shell interpolation on line 53 is acceptable since GitHub Actions inputs are sanitized by the platform.frontend/rollup.config.js (4)
41-43: LGTM! TLS connection optimization improves proxy performance.The
keepAliveconfiguration prevents redundant TLS handshakes by reusing connections to the backend, which is especially beneficial during development with frequent API requests.
67-74: LGTM! Socket timeout prevents hanging requests.The 2-second timeout with explicit request destruction ensures that the proxy fails fast when the backend is unreachable, improving developer experience during service restarts or backend issues.
78-81: LGTM! Conditional error response prevents runtime errors.Checking
res.headersSentbefore writing the error response prevents "Cannot set headers after they are sent to the client" errors, which would occur if the proxy response had already started streaming before the error occurred.
206-207: LGTM! Watch exclusions prevent unnecessary rebuilds.Excluding test artifacts (
test-results,playwright-report,e2e) from the watch list prevents Rollup from triggering rebuilds when test output files change, which improves the development experience when running tests alongside the dev server.frontend/src/App.svelte (4)
32-38: LGTM! Clean error state management with proper cleanup.The global error state subscription and cleanup in
onDestroyfollows Svelte best practices, ensuring no memory leaks from orphaned subscriptions.
55-67: LGTM! Well-structured authentication guard with redirect preservation.The
requireAuthhook properly:
- Waits for auth initialization to complete
- Preserves the intended destination for post-login redirect
- Excludes login/register routes from redirect preservation to avoid loops
- Returns a boolean to signal route access permission
69-85: LGTM! Clean routes configuration with clear protection boundaries.The consolidated routes array with inline
hooks: { pre: requireAuth }clearly distinguishes public routes (Home, Login, Register, Privacy) from protected routes, making it easy to understand and maintain the application's access control model.
88-106: LGTM! Proper error handling and loading states.The conditional rendering strategy correctly prioritizes:
- Global errors (showing
ErrorDisplayas a fallback UI)- Auth initialization (showing spinner while loading)
- Normal routing (rendering protected/public routes once ready)
This ensures users see appropriate feedback at each stage rather than encountering broken UI or unauthorized access errors.
frontend/src/routes/Login.svelte (1)
32-36: Non-blocking user settings load is safe and well-designed.The code poses no race condition risk. Editor.svelte (lines 234, 276) explicitly awaits
loadUserSettings()before accessing editor settings and does not assume pre-loaded state. The non-blocking call in Login.svelte populates the cache (with 5-minute TTL) for faster access; if the load completes after navigation to /editor, the cache is still available for the explicit await. Error handling gracefully defaults to undefined when settings are unavailable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
backend/workers/dlq_processor.py (1)
104-104: Verify test database isolation (same concern as other workers).This worker now uses
settings.DATABASE_NAMEunconditionally. Ensure test configurations override this setting appropriately to avoid writing to production databases.backend/workers/run_saga_orchestrator.py (1)
34-34: Verify test database isolation (same concern as other workers).This worker now uses
settings.DATABASE_NAMEunconditionally. Ensure test configurations override this setting appropriately.backend/app/services/coordinator/coordinator.py (1)
557-557: Verify test database isolation in coordinator service.The coordinator now uses
settings.DATABASE_NAMEunconditionally. Since this is a critical service handling execution scheduling, it's especially important to ensure test configurations properly isolate the test database.
🧹 Nitpick comments (1)
backend/app/settings.py (1)
12-12: Consider adding test database safeguards.The new
DATABASE_NAMEfield centralizes database configuration, which improves consistency. However, consider adding validation to prevent accidental production database usage in test environments.💡 Suggested safeguard implementation
Add a validator to ensure test safety:
from pydantic import field_validator class Settings(BaseSettings): PROJECT_NAME: str = "integr8scode" DATABASE_NAME: str = "integr8scode_db" # ... other fields ... TESTING: bool = False @field_validator('DATABASE_NAME') @classmethod def validate_test_database(cls, v: str, info) -> str: """Warn if TESTING=True but database name doesn't indicate test database""" testing = info.data.get('TESTING', False) if testing and 'test' not in v.lower(): import logging logging.warning( f"TESTING=True but DATABASE_NAME='{v}' doesn't contain 'test'. " f"Ensure you're not using a production database in tests." ) return vNote: Since
TESTINGis defined afterDATABASE_NAMEin the class, you may need to adjust field ordering or validation logic.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
backend/app/core/providers.pybackend/app/services/coordinator/coordinator.pybackend/app/services/k8s_worker/worker.pybackend/app/settings.pybackend/scripts/seed_users.pybackend/workers/dlq_processor.pybackend/workers/run_event_replay.pybackend/workers/run_saga_orchestrator.pyfrontend/src/components/ErrorDisplay.sveltefrontend/src/main.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/src/components/ErrorDisplay.svelte
- backend/app/core/providers.py
- backend/app/services/k8s_worker/worker.py
- backend/scripts/seed_users.py
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/main.ts (2)
frontend/src/lib/api/client.gen.ts (1)
client(16-16)frontend/src/stores/errorStore.ts (1)
appError(26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (2)
frontend/src/main.ts (2)
15-26: Previous concern addressed - consistent error handling implemented.The inconsistency flagged in the previous review has been resolved. Both error handlers now consistently prevent default browser error handling:
window.onerrorreturnstrue(line 19)window.onunhandledrejectioncallsevent.preventDefault()(line 25)This ensures uniform behavior for both synchronous and asynchronous unhandled errors.
28-44: Robust mount error handling implementation.The guarded mount pattern is well-implemented:
- Try/catch ensures graceful degradation if App mount fails
- ErrorDisplay provides user-visible feedback for mount failures
- Defensive
instanceof Errorcheck with fallback handles edge casesThis significantly improves the user experience by preventing silent initialization failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
backend/tests/conftest.py (1)
93-94: Consider making the test database name prefix configurable.The current implementation hardcodes the
"integr8scode_test"prefix when generating unique test database names. For better flexibility and maintainability, consider deriving the base name from the environment (e.g., reading a baseDATABASE_NAMEfrom.env.test) and appending the session/worker IDs.🔎 Proposed refactor to make prefix configurable
# Unique database name for test isolation - os.environ["DATABASE_NAME"] = f"integr8scode_test_{session_id}_{worker_id}" + base_db_name = os.environ.get("DATABASE_NAME", "integr8scode_test") + # Strip any existing session/worker suffix to get clean base name + base_clean = base_db_name.split("_test_")[0] if "_test_" in base_db_name else base_db_name + os.environ["DATABASE_NAME"] = f"{base_clean}_test_{session_id}_{worker_id}"This allows
.env.testto control the base database name while still ensuring unique isolation per test session/worker.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
backend/.envbackend/.env.testbackend/tests/conftest.py
🧰 Additional context used
🪛 dotenv-linter (4.0.0)
backend/.env
[warning] 2-2: [UnorderedKey] The DATABASE_NAME key should go before the PROJECT_NAME key
(UnorderedKey)
backend/.env.test
[warning] 3-3: [UnorderedKey] The DATABASE_NAME key should go before the PROJECT_NAME key
(UnorderedKey)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (1)
backend/.env.test (1)
2-3: LGTM! Test configuration updated appropriately.The changes normalize
PROJECT_NAMEtointegr8scodeand introduceDATABASE_NAME=integr8scode_testfor test environments. Note thatconftest.pydynamically generates a uniqueDATABASE_NAMEper test session/worker (e.g.,integr8scode_test_<session>_<worker>), so this static value serves as a fallback or base configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
backend/.env (1)
1-2: Reorder keys alphabetically for consistency.The dotenv-linter tool suggests placing
DATABASE_NAMEbeforePROJECT_NAMEto maintain alphabetical ordering across the file. While not critical, this improves consistency and readability.🔎 Proposed fix
-PROJECT_NAME=integr8scode DATABASE_NAME=integr8scode_db +PROJECT_NAME=integr8scode
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/.env
🧰 Additional context used
🪛 dotenv-linter (4.0.0)
backend/.env
[warning] 2-2: [UnorderedKey] The DATABASE_NAME key should go before the PROJECT_NAME key
(UnorderedKey)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Frontend E2E Tests
- GitHub Check: Backend Integration Tests
🔇 Additional comments (1)
backend/.env (1)
2-2: ✅ DATABASE_NAME value resolved—uses non-test database name.The change from the previous
integr8scode_testtointegr8scode_dbcorrectly aligns the main development.envwith the settings module default, preventing accidental writes to the test database during development. This resolves the earlier flagged concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
.github/workflows/frontend-ci.yml (3)
75-109: Consider extracting shared k3s setup to a reusable workflow or composite action.The k3s cluster setup (lines 75-82), kubeconfig creation (lines 84-104), and CI compose setup (lines 106-109) are nearly identical to
backend-ci.yml. Extracting this to a composite action would reduce duplication and simplify maintenance.This is acceptable as-is if you prefer keeping workflows self-contained and independent.
141-145: Consider adding a timeout for E2E tests.Unlike the backend integration tests (which have
timeout-minutes: 5), this step has no timeout. E2E tests can hang indefinitely if something goes wrong. Consider adding a timeout to prevent runaway jobs.🔎 Proposed fix
- name: Run E2E tests working-directory: frontend + timeout-minutes: 10 env: CI: true run: npx playwright test --reporter=html
154-160: Consider collecting Kubernetes logs on failure.The backend workflow collects k8s events and pod descriptions on failure (lines 133-134 in backend-ci.yml). Since the E2E job also uses k3s, these logs could be valuable for debugging.
🔎 Proposed addition
- name: Collect logs if: failure() run: | mkdir -p logs docker compose -f docker-compose.ci.yaml logs > logs/docker-compose.log docker compose -f docker-compose.ci.yaml logs backend > logs/backend.log docker compose -f docker-compose.ci.yaml logs frontend > logs/frontend.log + kubectl get events --sort-by='.metadata.creationTimestamp' > logs/k8s-events.log 2>&1 || true + kubectl describe pods -A > logs/k8s-describe-pods.log 2>&1 || true
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/backend-ci.yml.github/workflows/frontend-ci.ymlREADME.md
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/backend-ci.yml
[medium] 109-110: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (8)
README.md (1)
18-29: LGTM!The badge updates correctly reflect the new separate CI workflows for backend and frontend, with appropriate Codecov flags matching those defined in the respective workflow files.
.github/workflows/backend-ci.yml (4)
1-16: LGTM!The workflow triggers are well-configured with appropriate path filters to ensure the workflow only runs when relevant files change, including the workflow file itself and the CI compose configuration.
101-124: LGTM!Good test isolation using
SCHEMA_SUBJECT_PREFIXwith the run ID, and appropriate coverage configuration. Thefail_ci_if_error: falsefor Codecov is reasonable to prevent external service issues from blocking CI.
136-141: Use@v6as specified—this is the current stable version.actions/upload-artifact@v6 runs on Node.js 24 and requires a minimum Actions Runner version of 2.327.1. If using self-hosted runners, ensure they meet this requirement before using v6.
Likely an incorrect or invalid review comment.
23-23: actions/checkout@v6 is correct. Version 6 is the current stable release; no workflow failure risk exists. The latest version is v6.0.1.Likely an incorrect or invalid review comment.
.github/workflows/frontend-ci.yml (3)
18-49: LGTM!The unit tests job is well-structured with proper Node.js caching, dependency installation, and Codecov integration using the
frontendflag that matches the README badge configuration.
68-70: Consider documenting browser coverage strategy.Installing only
chromiumis efficient for CI. If cross-browser testing is needed later, consider adding--with-depsor other browsers. This is fine for now.
137-139: No action required—scripts/seed_users.pyexists and is properly implemented with idempotent upsert logic that safely handles re-runs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
.github/workflows/backend-ci.yml (1)
88-100: Consider using repository secrets or variables for CI credentials.The hardcoded MongoDB credentials (
root/rootpassword) triggered a static analysis warning (CKV_SECRET_4). While acceptable for ephemeral CI environments, using GitHub Actions secrets or variables would:
- Silence security scanners
- Make credential rotation easier
- Follow the principle of least privilege
This is a minor improvement and not blocking since these are clearly test-only credentials for an isolated CI environment.
🔎 Suggested approach
- name: Run integration tests timeout-minutes: 5 env: BACKEND_BASE_URL: https://127.0.0.1:443 - MONGO_ROOT_USER: root - MONGO_ROOT_PASSWORD: rootpassword + MONGO_ROOT_USER: ${{ vars.CI_MONGO_USER || 'root' }} + MONGO_ROOT_PASSWORD: ${{ secrets.CI_MONGO_PASSWORD || 'rootpassword' }} MONGODB_HOST: 127.0.0.1 MONGODB_PORT: 27017 - MONGODB_URL: mongodb://root:[email protected]:27017/?authSource=admin + MONGODB_URL: mongodb://${{ vars.CI_MONGO_USER || 'root' }}:${{ secrets.CI_MONGO_PASSWORD || 'rootpassword' }}@127.0.0.1:27017/?authSource=admin
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/backend-ci.yml
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/backend-ci.yml
[medium] 96-97: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (7)
.github/workflows/backend-ci.yml (7)
1-16: LGTM!Workflow triggers are well-configured with appropriate path filters for backend changes and the CI compose file. Manual dispatch is a good addition for debugging.
18-35: LGTM!Good use of
uvfor fast dependency management with proper caching configuration and--frozenflag for reproducible builds.
40-47: LGTM!Good k3s setup with
--tls-san host.docker.internalfor Docker container connectivity. The kubeconfig is exported to the standard~/.kube/configlocation, so subsequentkubectlcommands will work even though theexport KUBECONFIGon line 46 is step-scoped.
49-56: Previous concern resolved - now using real k3s credentials.The kubeconfig creation now correctly copies the actual k3s-generated credentials and substitutes the server address for Docker container networking. The inline comment explains the approach well.
58-80: LGTM!Good build configuration with multi-scope GHA caching (reads from current branch and main, writes to current branch) and
--remove-orphansfor clean service startup.
102-111: LGTM!Good Codecov configuration with
if: always()to capture coverage from failed runs andfail_ci_if_error: falsefor CI resilience.
113-128: LGTM!Comprehensive log collection on failure with appropriate error suppression (
|| true) to ensure all available logs are captured. Separate log files for each service will aid debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
.github/workflows/backend-ci.yml (1)
88-100: Consider using secrets for test credentials (optional).The hardcoded MongoDB credentials in the environment variables are flagged by static analysis (CKV_SECRET_4). While these are CI-only test credentials with no production impact, using GitHub secrets would be slightly more hygienic and prevent credential patterns from being copied elsewhere.
Since this is an ephemeral CI environment, the security risk is minimal, so this is purely a stylistic preference.
🔎 Optional: Use secrets for credentials
env: BACKEND_BASE_URL: https://127.0.0.1:443 - MONGO_ROOT_USER: root - MONGO_ROOT_PASSWORD: rootpassword + MONGO_ROOT_USER: ${{ secrets.CI_MONGO_USER || 'root' }} + MONGO_ROOT_PASSWORD: ${{ secrets.CI_MONGO_PASSWORD || 'rootpassword' }} MONGODB_HOST: 127.0.0.1 MONGODB_PORT: 27017 - MONGODB_URL: mongodb://root:[email protected]:27017/?authSource=admin + MONGODB_URL: mongodb://${{ secrets.CI_MONGO_USER || 'root' }}:${{ secrets.CI_MONGO_PASSWORD || 'rootpassword' }}@127.0.0.1:27017/?authSource=admin
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/backend-ci.yml
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/backend-ci.yml
[medium] 96-97: Basic Auth Credentials
(CKV_SECRET_4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (6)
.github/workflows/backend-ci.yml (6)
1-23: Well-structured workflow triggers and job setup.The path filtering ensures the workflow only runs when backend-related files change, avoiding unnecessary CI runs. The checkout step is standard.
25-38: Good dependency management with caching.Using
uvwith cache dependency glob and frozen sync ensures reproducible builds. Buildx setup is standard.
40-56: Correct kubeconfig setup for Docker container networking.The k3s setup with
--tls-san host.docker.internaland the subsequent sed replacement of the server address is the correct approach for Docker container networking. The real k3s credentials are preserved while adjusting the endpoint. This properly addresses the previous review concern about placeholder tokens.
58-86: Robust build and health check setup.The GHA cache scoping with fallback to main branch is a good strategy for cache reuse. The health check with 60 retries at 5-second intervals (up to 5 minutes total) provides adequate time for services to start.
102-111: Good coverage upload configuration.Using
if: always()ensures coverage is uploaded even when tests fail, which is useful for debugging. Settingfail_ci_if_error: falseprevents Codecov availability issues from blocking merges.
113-128: Comprehensive failure diagnostics.The log collection covers all relevant sources (docker-compose, individual services, k8s events and pod descriptions). Using
|| truefor kubectl commands ensures the step completes even if the cluster is in a bad state. This will greatly aid debugging CI failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (18)
frontend/src/stores/__tests__/errorStore.test.ts (2)
16-82: Excellent coverage of setError scenarios.The tests thoroughly verify Error objects, strings, titles, logging, timestamps, and overwriting behavior.
Optional: Consider adding test for Error object with title
For completeness, you could add a test case combining an Error object with a custom title:
+ it('sets Error object with title', () => { + const error = new Error('Error message'); + appError.setError(error, 'Custom Title'); + + const current = get(appError); + expect(current).not.toBe(null); + expect(current?.error).toBe(error); + expect(current?.title).toBe('Custom Title'); + }); + it('logs error to console', () => {
99-118: LGTM! Subscription behavior thoroughly verified.The test correctly validates the subscription mechanism, including the initial value, subsequent updates, clearing, and unsubscribing.
Optional: Consider simplifying the type on Line 101
The conditional type inference is correct but verbose. If the
AppErrorinterface is exported fromerrorStore, you could simplify:- const values: (typeof appError extends { subscribe: (fn: (v: infer T) => void) => void } ? T : never)[] = []; + const values: (AppError | null)[] = [];This would require adding to your imports:
-import { appError } from '../errorStore'; +import { appError, type AppError } from '../errorStore';frontend/src/utils/__tests__/meta.test.ts (1)
98-105: Consider expanding empty string test coverage.The current test verifies that an empty string doesn't update the title, but doesn't check description behavior. Consider also verifying:
- That no meta description tag is created when an empty description string is passed
- Adding a separate test case for calling
updateMetaTags()with no arguments🔎 Suggested additional test cases
it('handles empty strings', () => { document.title = 'Original'; updateMetaTags('', ''); // Empty string is falsy, so title should not change expect(document.title).toBe('Original'); + + // Empty string is also falsy for description, so no meta tag should be created + const meta = document.querySelector('meta[name="description"]'); + expect(meta).toBe(null); }); + + it('handles no arguments', () => { + document.title = 'Original'; + + updateMetaTags(); + + // Nothing should change when no arguments provided + expect(document.title).toBe('Original'); + const meta = document.querySelector('meta[name="description"]'); + expect(meta).toBe(null); + }); });frontend/src/components/__tests__/Spinner.test.ts (1)
30-92: Consider parameterized tests to reduce duplication.The size and color prop tests follow identical patterns. You could use Vitest's
it.each()to make these more concise and maintainable:💡 Example refactor using it.each
describe('size prop', () => { - it('applies small size class', () => { - render(Spinner, { props: { size: 'small' } }); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('h-4')).toBe(true); - expect(svg.classList.contains('w-4')).toBe(true); - }); - - it('applies medium size class by default', () => { - render(Spinner); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('h-6')).toBe(true); - expect(svg.classList.contains('w-6')).toBe(true); - }); - - it('applies large size class', () => { - render(Spinner, { props: { size: 'large' } }); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('h-8')).toBe(true); - expect(svg.classList.contains('w-8')).toBe(true); - }); - - it('applies xlarge size class', () => { - render(Spinner, { props: { size: 'xlarge' } }); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('h-12')).toBe(true); - expect(svg.classList.contains('w-12')).toBe(true); - }); + it.each([ + { size: 'small', height: 'h-4', width: 'w-4' }, + { size: 'medium', height: 'h-6', width: 'w-6' }, + { size: 'large', height: 'h-8', width: 'w-8' }, + { size: 'xlarge', height: 'h-12', width: 'w-12' }, + ])('applies $size size class', ({ size, height, width }) => { + render(Spinner, { props: { size: size === 'medium' ? undefined : size } }); + const svg = screen.getByRole('status'); + expect(svg.classList.contains(height)).toBe(true); + expect(svg.classList.contains(width)).toBe(true); + }); }); describe('color prop', () => { - it('applies primary color by default', () => { - render(Spinner); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('text-primary')).toBe(true); - }); - - it('applies white color class', () => { - render(Spinner, { props: { color: 'white' } }); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('text-white')).toBe(true); - }); - - it('applies current color class', () => { - render(Spinner, { props: { color: 'current' } }); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('text-current')).toBe(true); - }); - - it('applies muted color class', () => { - render(Spinner, { props: { color: 'muted' } }); - - const svg = screen.getByRole('status'); - expect(svg.classList.contains('text-gray-400')).toBe(true); - }); + it.each([ + { color: 'primary', expected: 'text-primary' }, + { color: 'white', expected: 'text-white' }, + { color: 'current', expected: 'text-current' }, + { color: 'muted', expected: 'text-gray-400' }, + ])('applies $color color class', ({ color, expected }) => { + render(Spinner, { props: { color: color === 'primary' ? undefined : color } }); + const svg = screen.getByRole('status'); + expect(svg.classList.contains(expected)).toBe(true); + }); });frontend/src/components/__tests__/ToastContainer.test.ts (2)
127-148: Consider extracting timer-switching pattern.The test manually switches between fake and real timers. This pattern is repeated in other tests (e.g., line 266-285). Consider extracting this into a helper function or using a dedicated test suite with real timers to improve consistency.
Example approach
describe('close button (with real timers)', () => { beforeEach(() => { vi.useRealTimers(); toasts.set([]); }); afterEach(() => { toasts.set([]); }); it('removes toast when close button clicked', async () => { // test implementation without timer switching }); });
209-232: Strengthen progress bar assertion.The test stores
initialTransform(line 221) but never uses it. The assertion only checks that the transform contains 'scaleX', not that the progress actually decreased.🔎 Proposed improvement
const alert = screen.getByRole('alert'); const timer = alert.querySelector('.timer') as HTMLElement; - const initialTransform = timer.style.transform; // Advance time await vi.advanceTimersByTimeAsync(1000); - // Check that transform has changed (progress decreased) await waitFor(() => { const newTransform = timer.style.transform; - // Both should have scaleX but with different values - expect(newTransform).toContain('scaleX'); + // Progress should have decreased (scaleX value should be less than 1) + const scaleXMatch = newTransform.match(/scaleX\(([\d.]+)\)/); + expect(scaleXMatch).toBeTruthy(); + expect(parseFloat(scaleXMatch![1])).toBeLessThan(1); });docs/testing/frontend-testing.md (1)
36-75: Add language specifier to the fenced code block.The directory tree structure should have a language specifier for proper rendering and syntax highlighting.
🔎 Proposed fix
-``` +```text src/ ├── stores/ │ ├── auth.tsfrontend/src/components/__tests__/NotificationCenter.test.ts (1)
24-132: Complex mock setup with type casting workarounds.The hoisted mock setup with manual store implementations and type casting workarounds (
(null as unknown) as ReturnType<...>) is necessary but adds complexity. The pattern is correct for Vitest's hoisting constraints, but consider:
- The manual store implementation duplicates Svelte store logic—future changes to store behavior might require updates here.
- The two-phase initialization (hoisted nulls, then vi.fn() assignment) is a known Vitest limitation.
If this pattern is repeated across multiple test files, consider extracting a shared test utility factory for creating mock stores to reduce duplication and maintenance burden.
frontend/src/components/__tests__/Header.test.ts (6)
4-4: Unused imports detected.
tickandflushSyncare imported but never used in this test file. Consider removing them to keep the imports clean.Proposed fix
-import { tick, flushSync } from 'svelte';
62-81: Component mock relies on Svelte internals.This mock structure works for testing but depends on Svelte's internal
$$object shape. Consider adding a comment noting this may need updates if Svelte's internal API changes in future versions.
153-166: Consider cleaning upmatchMediamock in afterEach.The
matchMediamock is set inbeforeEachbut not restored inafterEach. While this likely doesn't cause issues within this test file, it could affect other test files if they run in the same Vitest worker. For completeness, consider storing and restoring the originalmatchMediaimplementation.Proposed fix
describe('Header', () => { let originalInnerWidth: number; + let originalMatchMedia: typeof window.matchMedia; beforeEach(() => { // ... existing code ... // Store original window width originalInnerWidth = window.innerWidth; + originalMatchMedia = window.matchMedia; // ... rest of beforeEach ... }); afterEach(() => { // Restore window width Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: originalInnerWidth, }); + + // Restore matchMedia + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: originalMatchMedia, + }); });
215-246: SVG path assertions are coupled to implementation details.The tests check specific SVG path data (e.g.,
'M12 3v1','M20.354'). While functional, these assertions are brittle—any icon library update or SVG optimization could break them. Consider addingdata-testidattributes to the icons in the component for more resilient testing.
286-294: CSS class selector in test assertion.The filter
!l.closest('.lg\\:hidden')couples the test to specific Tailwind CSS class names. This works but may break if the styling approach changes. Consider usingaria-hiddenordata-testidattributes to distinguish desktop vs. mobile elements.
466-470: Non-null assertion without prior null check.Using
menuButton!assumes the element exists butexpect(menuButton).toBeTruthy()on line 468 only checks truthiness, it doesn't guard the click on line 470. IfmenuButtonis null, the assertion would fail with an unclear error from the click, not from the expect. Consider restructuring:Proposed fix
const mobileMenuWrapper = container.querySelector('.block.lg\\:hidden'); const menuButton = mobileMenuWrapper?.querySelector('button'); - expect(menuButton).toBeTruthy(); - - await user.click(menuButton!); + expect(menuButton).not.toBeNull(); + if (!menuButton) throw new Error('Menu button not found'); + + await user.click(menuButton);Alternatively, use
as HTMLButtonElementafter the assertion for cleaner TypeScript narrowing.frontend/src/components/__tests__/ProtectedRoute.test.ts (4)
48-57: Consider resettinginitializeandisAuthenticatedmocks if used in future tests.The
initializeandisAuthenticatedmocks insideAuthInitializeraren't reset inbeforeEach. Currently this is fine since these aren't called in tests, but if future tests call and assert on them, add resets to avoid state leakage between tests.
142-158: Consider initializingresolveWaitForInitto avoid non-null assertion.The variable is assigned inside the Promise executor (which runs synchronously), so the
!assertion works. However, initializing it upfront improves clarity and avoids potential issues if the code is refactored:Suggested improvement
- it('shows spinner while auth is initializing', async () => { - // Keep waitForInit pending - let resolveWaitForInit: () => void; - mocks.mockWaitForInit.mockImplementation(() => new Promise(resolve => { - resolveWaitForInit = () => resolve(true); - })); + it('shows spinner while auth is initializing', async () => { + // Keep waitForInit pending + let resolveWaitForInit: () => void = () => {}; + mocks.mockWaitForInit.mockImplementation(() => new Promise(resolve => { + resolveWaitForInit = () => resolve(true); + })); // ... - resolveWaitForInit!(); + resolveWaitForInit(); });
177-201: Fixed timeout delays can cause test flakiness.Using
setTimeout(resolve, 50)to wait for "something that shouldn't happen" is fragile—it may flake on slow CI or waste time in fast environments. Consider using Vitest's fake timers or increasing the timeout with a comment explaining the rationale:Alternative with fake timers
it('does not redirect when authenticated', async () => { vi.useFakeTimers(); render(ProtectedRoute); await waitFor(() => { expect(mocks.mockWaitForInit).toHaveBeenCalled(); }); // Advance timers to ensure no delayed redirect await vi.advanceTimersByTimeAsync(100); expect(mocks.mockGoto).not.toHaveBeenCalled(); vi.useRealTimers(); });
323-351: Visual state tests are implementation-coupled but acceptable.Testing for specific CSS classes like
.min-h-screen.flex.items-center.justify-centercouples tests to Tailwind implementation. This is a reasonable tradeoff for component-level tests, but be aware that styling refactors will require test updates.Consider adding
data-testidattributes for more stable selectors if styling changes frequently.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
docs/testing/frontend-testing.mdfrontend/src/components/__tests__/ErrorDisplay.test.tsfrontend/src/components/__tests__/Footer.test.tsfrontend/src/components/__tests__/Header.test.tsfrontend/src/components/__tests__/NotificationCenter.test.tsfrontend/src/components/__tests__/ProtectedRoute.test.tsfrontend/src/components/__tests__/Spinner.test.tsfrontend/src/components/__tests__/ToastContainer.test.tsfrontend/src/lib/__tests__/auth-init.test.tsfrontend/src/lib/__tests__/settings-cache.test.tsfrontend/src/lib/__tests__/user-settings.test.tsfrontend/src/stores/__tests__/errorStore.test.tsfrontend/src/utils/__tests__/meta.test.tsmkdocs.yml
🧰 Additional context used
🧬 Code graph analysis (6)
frontend/src/components/__tests__/ToastContainer.test.ts (1)
frontend/src/stores/toastStore.ts (3)
toasts(13-13)addToast(16-25)removeToast(27-29)
frontend/src/lib/__tests__/auth-init.test.ts (2)
frontend/src/components/__tests__/ProtectedRoute.test.ts (1)
AuthInitializer(50-56)frontend/src/lib/auth-init.ts (3)
initializeAuth(175-175)waitForAuth(176-176)checkAuth(177-177)
frontend/src/stores/__tests__/errorStore.test.ts (1)
frontend/src/stores/errorStore.ts (1)
appError(26-26)
frontend/src/utils/__tests__/meta.test.ts (1)
frontend/src/utils/meta.ts (2)
updateMetaTags(6-20)pageMeta(22-39)
frontend/src/lib/__tests__/settings-cache.test.ts (1)
frontend/src/lib/settings-cache.ts (5)
getCachedSettings(15-35)setCachedSettings(37-50)settingsCache(13-13)clearCache(52-56)updateCachedSetting(58-76)
frontend/src/lib/__tests__/user-settings.test.ts (1)
frontend/src/lib/user-settings.ts (3)
saveThemeSetting(14-36)loadUserSettings(38-65)saveEditorSettings(67-89)
🪛 LanguageTool
docs/testing/frontend-testing.md
[uncategorized] ~26-~26: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...erage End-to-end tests require the full stack running locally: bash npm run test:...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[grammar] ~266-~266: Ensure spelling is correct
Context: ...ests run: npm run test:e2e ``` Tests timeout after 5 minutes for unit tests and 10 m...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.18.1)
docs/testing/frontend-testing.md
36-36: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Scan Frontend
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (28)
frontend/src/stores/__tests__/errorStore.test.ts (2)
1-14: LGTM! Clean test setup.Proper test hygiene with store clearing, console mocking, and cleanup in afterEach.
84-97: LGTM! Clear method well-tested.Good coverage of both clearing an existing error and the idempotent case when already null.
frontend/src/utils/__tests__/meta.test.ts (2)
8-37: LGTM! Excellent test isolation.The setup and teardown logic properly preserves and restores document state, preventing test pollution. This is a best practice for DOM manipulation tests.
108-152: LGTM! Comprehensive and well-structured tests.The pageMeta tests demonstrate excellent practices:
- Individual tests verify expected content for each page
- The comprehensive test (lines 133-140) dynamically validates all entries with helpful assertion messages
- The integration test (lines 142-151) confirms real-world usage patterns
This ensures both the current page definitions and any future additions are properly validated.
frontend/src/components/__tests__/Spinner.test.ts (1)
1-136: Excellent test coverage and organization!This test suite is comprehensive and well-structured. It covers:
- Core rendering and accessibility (SVG element, role, label)
- All size variants with correct class applications
- All color variants with proper styling
- Custom className handling and composition
- SVG structure validation (viewBox, child elements)
The nested describe blocks make the test organization clear and maintainable.
frontend/src/components/__tests__/ToastContainer.test.ts (3)
8-21: LGTM!The Element.animate mock is comprehensive and correctly handles Svelte transitions. The implementation provides all required methods and properties for the Web Animations API.
23-33: LGTM!The test setup and teardown properly isolate tests by using fake timers and clearing the toast store between tests.
35-114: LGTM!The rendering and toast type tests provide good coverage of the component's visual states and styling variants.
mkdocs.yml (1)
138-138: LGTM!The navigation entry correctly references the new frontend testing documentation.
frontend/src/components/__tests__/NotificationCenter.test.ts (2)
154-190: LGTM!The Animation mock comprehensively implements the Web Animations API with automatic onfinish triggering, which is essential for testing Svelte transitions in jsdom. The pattern of immediately calling onfinish in the setter (line 160) ensures transitions complete synchronously in tests.
226-865: LGTM!The test suite provides comprehensive coverage of the NotificationCenter component:
- UI rendering and visual states
- User interactions (click, keyboard)
- Accessibility attributes
- Edge cases (9+ badge, empty states)
- Time formatting logic
The tests are well-organized with clear describe blocks and use realistic user event simulation.
frontend/src/components/__tests__/Footer.test.ts (1)
1-179: LGTM!The Footer test suite is well-structured and comprehensive:
- Date mocking ensures stable year assertions (2025)
- Tests verify branding, navigation, external links, social media, copyright
- Accessibility checks include semantic HTML, heading hierarchy, and security attributes (noopener/noreferrer)
- Responsive design validation
The tests follow testing-library best practices by querying elements by role and accessible names.
frontend/src/components/__tests__/ErrorDisplay.test.ts (2)
82-88: LGTM! Important security test.This test verifies that raw error details (including potentially sensitive information like connection strings) are never exposed to users. This is a critical security measure that prevents information leakage.
1-151: LGTM!The ErrorDisplay test suite comprehensively covers:
- Rendering with different error types (string, Error object)
- User-friendly message mapping for network vs. generic errors
- Security: preventing sensitive data exposure
- Action button behaviors (reload, navigate home)
- Visual elements and styling
The window.location mocking setup is correct and properly restored in afterEach.
frontend/src/lib/__tests__/settings-cache.test.ts (1)
1-206: LGTM!The settings-cache test suite provides thorough coverage:
- Cache retrieval with TTL expiration logic
- Persistence to localStorage with timestamp
- Cache updates for nested settings paths
- Store initialization from localStorage
- Error handling for parse errors and storage exceptions
The use of
vi.resetModules()ensures proper isolation between tests by clearing module-level state.frontend/src/lib/__tests__/auth-init.test.ts (1)
1-372: LGTM!The auth-init test suite is exceptionally comprehensive, covering:
- Basic initialization flows (no auth, verification success/failure)
- Idempotent initialization (only one verification call)
- Concurrent initialization handling
- Persisted auth restoration and verification
- Time-based staleness logic (24h expiry, 5min network error grace period)
- Error handling (malformed JSON, settings loading failures)
- Static method behavior and exported function delegation
The matchMedia helper setup (lines 41-55) and module reset pattern ensure proper test isolation.
frontend/src/lib/__tests__/user-settings.test.ts (1)
1-287: LGTM!The user-settings test suite comprehensively covers:
- Authentication gating (returns undefined when not authenticated)
- API interactions for theme and editor settings
- Cache behavior (return cached settings, fallback to API)
- Theme application via setThemeLocal
- Error handling for API and network failures
- Edge case: settings without theme field
The mock setup properly isolates the module under test while maintaining realistic behavior of dependencies.
frontend/src/components/__tests__/Header.test.ts (4)
6-39: Well-structured hoisted mock setup.The
createMockStorefactory provides a clean, minimal implementation of Svelte's store contract with proper subscriber management. Usingvi.hoistedensures these mocks are available before ES module imports execute, which is the correct pattern for mocking stores in Vitest.
41-60: Good use of getter-based mocks for test isolation.Using getters to defer access to the hoisted mocks ensures that
mockReset()calls inbeforeEachproperly affect subsequent test runs. This is a solid pattern for Svelte store mocking with Vitest.
83-120: Comprehensive animation mock for Svelte transitions.The mock correctly implements the full Animation interface. The
setTimeout(fn, 0)foronfinishsimulates instant animation completion, which works well with thewaitForassertions used throughout the tests.
386-443: Good admin-specific test coverage.The tests properly handle the edge case where the username "admin" could conflict with the "Admin" button text by using case-sensitive regex matching (
/^Admin$/). Well-documented with inline comments explaining the approach.frontend/src/components/__tests__/ProtectedRoute.test.ts (7)
1-36: Well-structured mock hoisting pattern.The use of
vi.hoisted()with deferredvi.fn()initialization is a valid workaround for Vitest's hoisting constraints. ThecreateMockStorecorrectly implements Svelte's store contract withsubscribe,set, andupdatemethods.
86-139: Good test isolation with thorough mock setup and teardown.The
beforeEach/afterEachhooks properly reset stores, mocks, and browser globals. Saving and restoringoriginalSessionStorageensures clean isolation between tests.
203-274: Comprehensive unauthenticated state coverage.Tests cover redirect behavior, custom paths, sessionStorage interactions for redirect URLs and auth messages, and edge cases like not saving login/register paths. Good separation of concerns across test cases.
276-297: Good reactive behavior test for auth state changes.This test verifies the component properly responds to store updates by simulating a logout scenario. The flow (verify no redirect → change state → verify redirect) is clear and correct.
299-321: Props handling tests provide clear documentation.These tests explicitly document prop behavior. Note there's some overlap with unauthenticated state tests—consider consolidating if the test suite grows large, but the current duplication aids readability.
365-383: Rapid state changes test verifies stability, not true concurrency.Since JavaScript is single-threaded, the three
set()calls execute synchronously before any reactive effects run. This tests that the component handles multiple synchronous updates without crashing, and correctly responds to the final state (false). The naming is slightly misleading but the test is valid.
385-396: Verify empty message handling matches component implementation.This test expects that an empty string
messageprop results in noauthMessagebeing saved.ProtectedRoute.sveltecorrectly implements this filtering logic at lines 38-40 withif (message) sessionStorage.setItem('authMessage', message), ensuring empty messages are not persisted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (12)
frontend/src/stores/__tests__/auth.test.ts (2)
150-159: Consider asserting the specific error for failed login.The test verifies an error is thrown but doesn't assert its content. This could mask incorrect error handling.
🔎 Suggested improvement
it('throws error on failed login', async () => { mockLoginApi.mockResolvedValue({ data: null, error: { detail: 'Invalid credentials' }, }); const { login } = await import('../auth'); - await expect(login('baduser', 'badpass')).rejects.toBeDefined(); + await expect(login('baduser', 'badpass')).rejects.toMatchObject({ + detail: 'Invalid credentials', + }); });
351-360: Consider asserting the specific error for failed profile fetch.Similar to the login test, asserting the actual error structure would make the test more robust.
🔎 Suggested improvement
it('throws error on failed profile fetch', async () => { mockGetProfileApi.mockResolvedValue({ data: null, error: { detail: 'Unauthorized' }, }); const { fetchUserProfile } = await import('../auth'); - await expect(fetchUserProfile()).rejects.toBeDefined(); + await expect(fetchUserProfile()).rejects.toMatchObject({ + detail: 'Unauthorized', + }); });frontend/src/components/__tests__/ProtectedRoute.test.ts (3)
119-136: Possible uninitialized variable warning.
resolveWaitForInitmay be used before assignment if the Promise executor runs asynchronously (though in practice it runs synchronously). Adding definite assignment assertion or initializing with a no-op would satisfy stricter linting.🔎 Suggested improvement
it('shows spinner while auth is initializing', async () => { // Keep waitForInit pending - let resolveWaitForInit: () => void; + let resolveWaitForInit: () => void = () => {}; mocks.mockWaitForInit.mockImplementation(() => new Promise(resolve => { resolveWaitForInit = () => resolve(true); }));
155-166: Arbitrary timeout in test may cause flakiness.Using
setTimeout(resolve, 50)for waiting is fragile and can cause intermittent failures in slow CI environments. Consider usingwaitForwith a condition orvi.advanceTimersByTimewith fake timers.🔎 Suggested improvement using fake timers
+ describe('authenticated state', () => { + beforeEach(() => { + vi.useFakeTimers(); mocks.mockIsAuthenticated.set(true); mocks.mockWaitForInit.mockResolvedValue(true); }); + + afterEach(() => { + vi.useRealTimers(); + }); it('does not redirect when authenticated', async () => { render(ProtectedRoute); await waitFor(() => { expect(mocks.mockWaitForInit).toHaveBeenCalled(); }); - // Give time for any potential redirect - await new Promise(resolve => setTimeout(resolve, 50)); + // Advance timers to ensure no pending redirect + await vi.advanceTimersByTimeAsync(100); expect(mocks.mockGoto).not.toHaveBeenCalled(); });
343-361: Rapid state change test validates reactivity but assertion is weak.The test simulates rapid auth toggling but only asserts that
gotowas eventually called. Consider asserting the final call count or specific call arguments to validate no duplicate redirects occur.🔎 Suggested improvement
it('handles rapid auth state changes', async () => { mocks.mockIsAuthenticated.set(true); mocks.mockWaitForInit.mockResolvedValue(true); render(ProtectedRoute); await waitFor(() => { expect(mocks.mockWaitForInit).toHaveBeenCalled(); }); // Rapidly toggle auth mocks.mockIsAuthenticated.set(false); mocks.mockIsAuthenticated.set(true); mocks.mockIsAuthenticated.set(false); await waitFor(() => { - expect(mocks.mockGoto).toHaveBeenCalled(); + expect(mocks.mockGoto).toHaveBeenCalledWith('/login'); }); + + // Verify no duplicate redirects + expect(mocks.mockGoto).toHaveBeenCalledTimes(1); });frontend/src/__tests__/test-utils.ts (2)
81-107: Edge case:createMockSvelteComponentmay break on self-closing tags.The
html.replace('>', ...)logic assumes the first>is the opening tag's closing bracket. Self-closing elements like<div />or elements with attributes containing>could produce malformed HTML.🔎 Suggested improvement using regex for safer replacement
export function createMockSvelteComponent(html: string, testId?: string): { default: { new (): object; render: () => { html: string; css: { code: string; map: null }; head: string } }; } { const htmlWithTestId = testId - ? html.replace('>', ` data-testid="${testId}">`) + ? html.replace(/^(<\w+)/, `$1 data-testid="${testId}"`) : html;
175-185:MOCK_STORE_TEMPLATEprovides copy-paste reference.While pragmatic, this approach requires manual synchronization if the template changes. Consider adding a version comment to help detect drift.
🔎 Suggested addition
+// Template version: 1.0 - Update tests if this changes export const MOCK_STORE_TEMPLATE = ` function createMockStore<T>(initial: T) {frontend/src/components/__tests__/NotificationCenter.test.ts (5)
6-22: Consider importing types instead of duplicating them.The
MockNotificationandMockNotificationStateinterfaces appear to duplicate types that likely exist in the source code or could be inferred from thecreateMockNotificationutility in test-utils. Duplicating type definitions increases maintenance burden when the source types change.Potential approach
If the actual notification types are exported from the source, import them:
import type { Notification, NotificationState } from '../../types/notifications';Or use TypeScript's
ReturnTypeutility to derive types from the test utility:type MockNotification = ReturnType<typeof createMockNotification>;
51-51: Extract the long inline type for readability.Line 51 contains an extremely long inline type definition that makes the code hard to read. While the type must remain in the hoisted block, you can extract it to a separate line for clarity.
🔎 Proposed refactor
+ type NotificationItem = { + notification_id: string; + subject: string; + body: string; + status: 'unread' | 'read'; + severity: 'low' | 'medium' | 'high' | 'urgent'; + tags: string[]; + created_at: string; + action_url?: string; + }; + - type NotifState = { notifications: Array<{ notification_id: string; subject: string; body: string; status: 'unread' | 'read'; severity: 'low' | 'medium' | 'high' | 'urgent'; tags: string[]; created_at: string; action_url?: string; }>; loading: boolean; error: string | null; }; + type NotifState = { + notifications: NotificationItem[]; + loading: boolean; + error: string | null; + };
174-177: Console suppression may hide legitimate errors during debugging.Suppressing all console output (log, debug, error) can make debugging test failures more difficult. If the component logs unexpected errors or warnings, they'll be silently hidden.
Consider suppressing only expected/noisy output, or remove console mocking unless specific noisy logs need to be silenced.
204-205: Avoid testing implementation details via CSS class selectors.Multiple tests use
.querySelector()with Tailwind CSS classes (.bg-red-500,.bg-blue-500,.text-orange-600, etc.) to locate elements. This creates fragile tests that will break if styling changes, even when functionality remains correct.Prefer semantic queries (roles, labels) or add
data-testidattributes for styling-related assertions that can't use semantic queries.Example alternative approaches
For the unread badge (lines 204-205, 221-223, 247-249):
// Add data-testid to the badge element in the component const badge = screen.queryByTestId('unread-badge');For visual states that must be verified:
// Use role + accessible name, then check computed styles if necessary const notification = screen.getByRole('button', { name: /View notification/ }); expect(notification).toHaveClass('bg-blue-50'); // Still checks class but through semantic query firstAlso applies to: 221-223, 247-249, 399-401, 412-414, 570-572, 591-593
502-505: Avoid asserting on SVG path data.These tests verify specific SVG path strings (
M9 12l2 2 4-4,M12 8v4m0 4h.01), which are implementation details. If the icon library is updated or different icons are chosen, tests will break even though the semantic meaning (success/error/warning icon) remains correct.Consider verifying icon semantics instead of implementation details, such as:
- An aria-label or title on the icon
- A data-testid attribute indicating icon type
- A CSS class indicating icon category
Example approach
// In the component, add semantic attributes: // <svg aria-label="Success icon" data-icon-type="success"> // In tests: const icon = screen.getByLabelText('Success icon'); // or const iconWrapper = container.querySelector('[data-icon-type="success"]'); expect(iconWrapper).toBeInTheDocument();Also applies to: 524-527, 546-549
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
frontend/src/__tests__/test-utils.tsfrontend/src/components/__tests__/Header.test.tsfrontend/src/components/__tests__/NotificationCenter.test.tsfrontend/src/components/__tests__/ProtectedRoute.test.tsfrontend/src/components/__tests__/ToastContainer.test.tsfrontend/src/stores/__tests__/auth.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/components/tests/Header.test.ts
- frontend/src/components/tests/ToastContainer.test.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/stores/__tests__/auth.test.ts (2)
frontend/src/stores/auth.ts (3)
csrfToken(48-48)verifyAuth(129-174)fetchUserProfile(95-103)frontend/src/__tests__/test-utils.ts (2)
suppressConsoleError(139-142)suppressConsoleWarn(148-151)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)
frontend/src/__tests__/test-utils.ts (2)
createMockNotification(194-223)setupAnimationMock(66-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Scan Frontend
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (14)
frontend/src/stores/__tests__/auth.test.ts (4)
1-16: Well-structured mock setup for API layer.The mocking approach correctly isolates the auth store from the actual API implementation. Using
vi.fn()references with wrappers invi.mockensures proper mock reset behavior.
21-40: Good test isolation with module reset pattern.Using
vi.resetModules()combined with dynamic imports ensures each test gets a fresh store state. This is the correct approach for testing Svelte stores that maintain module-level state.
46-99: Comprehensive initial state tests including localStorage restoration and expiry.Good coverage of edge cases including expired auth state cleanup. The 25-hour expiry test validates the auth persistence timeout mechanism.
305-330: Good offline-first behavior test.This test properly validates the security trade-off documented in
verifyAuth- returning cached values on network errors. The test setup withmockResolvedValueOncefollowed bymockRejectedValueOncecorrectly simulates the offline scenario.frontend/src/components/__tests__/ProtectedRoute.test.ts (3)
5-22: Correct use ofvi.hoistedfor mock store factory.The inline
createMockStorefunction is necessary becausevi.hoistedruns before imports. The type assertion on lines 19-20 formockWaitForInitandmockGotois a reasonable workaround for initializing them outside the hoisted context.
181-252: Thorough unauthenticated state test coverage.Good coverage including custom redirect paths, sessionStorage interactions, exclusion of login/register paths from redirect storage, and auth message handling.
363-374: Good edge case: empty message prop handling.Correctly validates that empty strings are not persisted to sessionStorage, preventing potential UI issues with blank messages.
frontend/src/__tests__/test-utils.ts (5)
1-7: Good documentation explaining thevi.hoistedlimitation.The module-level documentation clarifies why
createMockStoremust be duplicated in each test file rather than imported. This prevents confusion for future contributors.
19-60: Comprehensive Animation mock implementation.The mock correctly implements the full
Animationinterface including the criticalonfinishsetter that triggers immediately viasetTimeout(fn, 0). This ensures Svelte transitions complete synchronously in tests.
117-129: Type definitions support type-safe mock usage.The
MockStore<T>andMockDerivedStore<T>interfaces provide proper typing for stores created withinvi.hoistedblocks, enabling TypeScript support in test files.
139-151: Console suppression utilities follow RAII pattern.Returning a restore function ensures cleanup happens even if tests throw, provided callers use proper cleanup in
afterEachor try/finally blocks. The pattern used in auth.test.ts (restoreConsole()at end of test) is correct.
194-237: Well-designed test data factories.
createMockNotificationwith partial overrides andcreateMockNotificationsfor bulk creation follow established testing patterns. The alternatingunread/readstatus in bulk creation provides varied test data.frontend/src/components/__tests__/NotificationCenter.test.ts (2)
75-137: Well-structured mock setup.The mock initialization pattern using getters to defer access to hoisted mocks is appropriate, and the MockEventSource implementation correctly simulates the EventSource API behavior needed for testing.
156-790: Excellent test coverage and organization.The test suite provides comprehensive coverage of the NotificationCenter component:
- All major user flows (opening dropdown, marking as read, navigation)
- Edge cases (empty state, 9+ badge overflow)
- Keyboard navigation and accessibility
- Time formatting variations
The test organization is clear, and the use of helper functions (
openDropdownWithNotifications,notification) improves readability and reduces duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
frontend/src/components/__tests__/Header.test.ts (2)
158-189: Consider more robust icon verification.The tests verify theme icons by inspecting SVG path data strings. This approach is brittle—any minor change to the icon paths will break the tests. Consider alternative strategies:
- Add
data-testidordata-iconattributes to the SVG elements in the Header component- Check for icon-specific class names instead of path internals
- Use visual regression testing for icon appearance
229-237: Slightly confusing filter logic (optional improvement).Lines 235-236 use double-negative logic (
!l.closest('.lg\\:hidden')combined withexpect(...).toHaveLength(0)) to verify Login/Register don't appear on desktop. This works correctly but could be more readable. Consider:const desktopLoginLinks = screen.queryAllByRole('link', { name: /^Login$/i }) .filter(l => !l.closest('.lg\\:hidden')); expect(desktopLoginLinks).toHaveLength(0);Or use a more direct assertion:
const visibleLoginButton = screen.queryByRole('link', { name: /^Login$/i }); const isInMobileMenu = visibleLoginButton?.closest('.lg\\:hidden'); expect(visibleLoginButton === null || isInMobileMenu).toBe(true);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/src/components/__tests__/Header.test.tsfrontend/src/components/__tests__/ToastContainer.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/tests/ToastContainer.test.ts
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/__tests__/Header.test.ts (1)
frontend/src/__tests__/test-utils.ts (2)
setupAnimationMock(66-68)suppressConsoleError(139-142)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (3)
frontend/src/components/__tests__/Header.test.ts (3)
1-65: LGTM: Well-structured mock setup.The hoisted mock pattern with inline store factory and deferred getter access is correctly implemented. The mock ordering (mocks before component import) and Svelte 5 compatible component mock are appropriate.
67-119: LGTM: Thorough test setup and cleanup.The setup properly resets all mock state, configures window dimensions for responsive testing, and includes cleanup to restore window width. This ensures good test isolation.
505-534: Test correctly describes behavior.The test "closes dropdown when clicking a menu item" accurately describes the implementation, which clicks the Settings link (a menu item) and verifies the dropdown closes. The past review comment about mismatched test names appears to have been addressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (7)
frontend/src/components/__tests__/ToastContainer.test.ts (3)
63-78: Consider extracting timer-switching logic.The pattern of manually toggling
vi.useRealTimers()andvi.useFakeTimers()within the test body makes the test harder to follow. Consider using a helper function or reorganizing tests that need real timers into a separate describe block with its own beforeEach/afterEach.🔎 Example refactor using a separate describe block
+ describe('close button (real timers)', () => { + beforeEach(() => { + vi.useRealTimers(); + }); + + afterEach(() => { + vi.useFakeTimers(); + }); + - describe('close button', () => { it('renders close button and removes toast when clicked', async () => { - vi.useRealTimers(); render(ToastContainer); addToast('Closable toast', 'info'); await waitFor(() => { expect(screen.getByRole('alert')).toBeInTheDocument(); }); const closeButton = screen.getByRole('button', { name: /Close toast/i }); expect(closeButton).toBeInTheDocument(); closeButton.click(); await waitFor(() => { expect(get(toasts)).toHaveLength(0); }); - vi.useFakeTimers(); }); });
110-126: Apply consistent timer management pattern.This test has the same timer-switching pattern as the close button test. Consider grouping all tests that require real timers into a dedicated describe block to avoid manual timer toggling.
🔎 Example refactor
+ describe('integration with store (real timers)', () => { + beforeEach(() => { + vi.useRealTimers(); + }); + + afterEach(() => { + vi.useFakeTimers(); + }); + - describe('integration with store', () => { it('reflects store additions and removals', async () => { - vi.useRealTimers(); render(ToastContainer); expect(screen.queryByRole('alert')).not.toBeInTheDocument(); addToast('Added via store', 'success'); await waitFor(() => { expect(screen.getByText('Added via store')).toBeInTheDocument(); }); const [toast] = get(toasts); removeToast(toast.id); expect(get(toasts)).toHaveLength(0); - - vi.useFakeTimers(); }); });
80-95: Progress bar assertion could be more robust.Line 93 checks
timer.style.transformwhich assumes the transform is set as an inline style. If the animation applies the transform via CSS class or computed styles, this assertion might fail or be flaky.🔎 Consider using computed styles
it('shows progress bar that decreases over time', async () => { render(ToastContainer); addToast('Timed toast', 'info'); await waitFor(() => { const timer = screen.getByRole('alert').querySelector('.timer'); expect(timer).toBeInTheDocument(); }); const timer = screen.getByRole('alert').querySelector('.timer') as HTMLElement; await vi.advanceTimersByTimeAsync(1000); - await waitFor(() => { expect(timer.style.transform).toContain('scaleX'); }); + await waitFor(() => { + const styles = window.getComputedStyle(timer); + expect(styles.transform).toContain('scaleX'); + }); });frontend/src/components/__tests__/Header.test.ts (4)
6-27: Consider using Svelte'swritablestores for test mocks (optional).The custom
createMockStoreimplementation correctly replicates Svelte's store contract. However, ifvi.hoisted's self-containment constraint allows it, you could importwritablefromsvelte/storedirectly to reduce duplication and leverage the battle-tested implementation.If the hoisting restriction prevents external imports, the current approach is acceptable.
64-70: Non-null assertion without validation in test helper.Line 68 uses
menuButton!after an optional chain (?.querySelector). If the menu button isn't found, the test will fail with a cryptic error rather than a clear assertion failure.🔎 Suggested improvement
const openMobileMenu = async () => { const user = userEvent.setup(); const { container } = render(Header); const menuButton = container.querySelector('.block.lg\\:hidden')?.querySelector('button'); + if (!menuButton) throw new Error('Mobile menu button not found'); - await user.click(menuButton!); + await user.click(menuButton); return { user, container }; };
118-129: SVG path assertions are brittle (optional improvement).The test verifies theme icons by checking for specific SVG path fragments (lines 119-121). If the icon library updates or path coordinates change slightly, these tests will fail even though the correct icon is displayed visually.
Consider testing icon-related attributes like
aria-label,data-icon, CSS classes, ordata-testidvalues instead, which are more stable across icon library updates.
57-62: Static analysis flags RegExp from variable (likely false positive in tests).Line 59 constructs a RegExp from the
usernameparameter, which static analysis flags as a potential ReDoS vulnerability. In this test context, all username values are controlled string literals ('testuser', 'admin', etc.), so this is not a real security concern.If you want to eliminate the warning, you could use exact string matching:
Alternative implementation
const openUserDropdown = async (username: string) => { const user = userEvent.setup(); render(Header); - await user.click(screen.getByRole('button', { name: new RegExp(username, 'i') })); + await user.click(screen.getByRole('button', { name: username })); return user; };Note: This removes case-insensitive matching. If that's needed, you could use a predicate matcher instead.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/components/__tests__/Header.test.tsfrontend/src/components/__tests__/NotificationCenter.test.tsfrontend/src/components/__tests__/Spinner.test.tsfrontend/src/components/__tests__/ToastContainer.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/tests/Spinner.test.ts
🧰 Additional context used
🧬 Code graph analysis (3)
frontend/src/components/__tests__/ToastContainer.test.ts (2)
frontend/src/__tests__/test-utils.ts (1)
setupAnimationMock(66-68)frontend/src/stores/toastStore.ts (3)
toasts(13-13)addToast(16-25)removeToast(27-29)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)
frontend/src/__tests__/test-utils.ts (1)
setupAnimationMock(66-68)
frontend/src/components/__tests__/Header.test.ts (2)
frontend/src/components/__tests__/NotificationCenter.test.ts (1)
username(81-81)frontend/src/__tests__/test-utils.ts (2)
setupAnimationMock(66-68)suppressConsoleError(139-142)
🪛 ast-grep (0.40.3)
frontend/src/components/__tests__/Header.test.ts
[warning] 59-59: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(username, 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Scan Frontend
- GitHub Check: E2E Tests
- GitHub Check: Integration Tests
🔇 Additional comments (16)
frontend/src/components/__tests__/Header.test.ts (1)
225-235: Why is console error suppression needed here?Line 227 suppresses console errors for this test, but it's not clear what errors are expected. If the router or component legitimately logs errors during the Settings navigation, consider:
- Documenting why errors are expected (e.g., router navigation in test environment)
- Using a more specific assertion to verify the expected error is logged
- Fixing the underlying cause if errors indicate a problem
Can you clarify what console errors this test expects and whether they indicate an issue that should be addressed?
frontend/src/components/__tests__/NotificationCenter.test.ts (15)
1-16: Well-structured test setup with proper typing.The imports are appropriate for the testing scenario, and the
MockNotificationinterface provides good type safety for the mock data used throughout the tests.
18-63: Solid mock infrastructure usingvi.hoisted.The self-contained store implementations within
vi.hoistedproperly handle subscription patterns and derived state computation. This approach correctly ensures mocks are available before module resolution.
65-88: Correct mock initialization and module mocking.The getter pattern in
vi.mockcalls properly accesses the hoisted mocks, allowing them to be reset between tests while maintaining correct module behavior.
90-106: Adequate global mocks for EventSource and Notification APIs.The
MockEventSourceproperly simulates connection states and async open events. The minimalNotificationstub covers the permission-checking behavior needed for these tests.
109-139: Clean test helpers reduce boilerplate.The
createNotificationfactory andopenDropdownhelpers effectively centralize common setup, making individual tests more focused and readable.
141-159: Thorough test isolation with proper reset and restore.The
beforeEachproperly resets all mock state and functions, whileafterEachrestores spies. Console suppression keeps test output clean.
161-190: Good coverage of bell icon and badge behavior.The parameterized tests effectively cover the badge display logic, including the "9+" overflow case. Using
waitForcorrectly handles the async state updates.
192-215: Comprehensive dropdown behavior tests.Tests properly cover opening, empty state, navigation link, and the navigation action with route verification.
217-242: Well-structured notification list tests with representative sample data.The sample notifications cover different states and severities, and the tests properly verify both content rendering and visual indicators.
260-267: Correct verification of mark all as read action.The test properly sets up state, triggers the action, and verifies the store method was called.
269-282: Good parameterized icon verification.Testing SVG paths for different tag types ensures the correct icons are rendered for each notification category.
284-293: Priority color tests cover critical severity levels.The tests verify the most important visual distinctions for high and urgent priorities.
295-305: Time formatting tests are well-structured.The parameterized approach covers key time thresholds. The timing logic is straightforward, and the distinct offsets (0ms, 5min, 3hr) provide enough buffer to avoid flakiness in typical test execution.
307-333: Thorough click and keyboard interaction tests.The tests properly verify both the
markAsReadstore call and navigation for click and keyboard interactions. The keyboard navigation test correctly usesfocus()followed bykeyboard('{Enter}').
335-350: Essential accessibility checks in place.The tests verify proper
aria-labelattributes and keyboard focusability (tabindex="0"), ensuring the component meets basic accessibility requirements.
|



Summary by CodeRabbit
New Features
Tests
Chores
Configuration
✏️ Tip: You can customize this high-level summary in your review settings.